pthread_mutex_* 와 pthread_cond_*를 사용한 동기화문제

shiefra의 이미지

똑같은 main을 사용하는 두 thread간의 문제인데요.
코드는,

        do {
                
                . . . .

                pthread_mutex_lock (&mutex_lock);      // A
                if (!start)
                        pthread_cond_wait (&cond, &mutex_lock);   // B
                start=0;
                
                   C.S.

                pthread_mutex_unlock (&mutex_lock);  
                sleep(1);    
                pthread_cond_signal (&cond);    

        }while (...) 

시나리오는 이렇습니다.

1. 첫번째 thread가 A를 무사히 통과해서 CS를 실행하는 동안에 두번째 thread가 A에서 block 됩니다. (처음 thread는 cond_wait를 호출하지 않습니다.)

2. CS를 마친 첫번째 thread가 C에서 mutex를 unlock 하고, 두번째 thread는 mutex가 풀리면서 다음라인을 실행하다가 cond_wait에서 다시 block됩니다.

3. 이 때 첫번째 thread가 cond_signal을 호출해서 두번째 thread를 깨웁니다.

문제는 sleep(1) 부분인데요,
첫번째 thread가 cond_signal을 하기 전에
두번째 thread가 cond_wait를 했다는 보장이 없는것 같아서
sleep()를 쓴건데, 이 방법 말고 눈에 보이는 지연시간없이
두번째 thread가 cond_wait를 cond_signal 보다 먼저 하게 보장해줄 수 없을까요?

그리고, mutex랑 cond를 저렇게 쓰는게 맞는건지도 잘 모르겠습니다.
sleep(1) 포함해서 원하는대로 동작하는것 같지만 그래도 불안..

bugiii의 이미지

원하시는 동작이 쓰레드들간에 Critical Section 을 위해서 동기화 작업이 필요하신 것이라면 pthread_con_* 를 사용하실 필요가 없습니다. 그런 용도라면 단순히 mutex_lock / unlock 으로 충분합니다.

pthread_con_* 의 사용처는 mutex 와는 좀 다릅니다. condition variable 은 pthread_wait 로 대기하는 쓰레드를 원하는 상황이 오면 깨우기 위해서 사용됩니다.

mutex 는 쓰레드가 각각 동등한 입장에서 동일한 리소스를 한번에 억세스하지 못하도록 하는 것이고, cond. var. 은 한쪽은 이벤트를 만들고 다른 한쪽 (혹은 여러) 쓰레드는 대기하고 있다가 신호가 오면 깨어나도록 하는 것입니다.

이것의 대략적인 용법은 UNP 23 장이 도움이 될 것 같습니다.

simpid의 이미지

저의 경우 Windows 프로그래밍을 하다가 Linux쪽 프로그래밍을 하다보니
이 문제때문에 골치좀 아팠었습니다.

pthread 이넘... WIN32에 익숙한 프로그래머 입장에서 좀 짜증나죠.

pthread_cond_wait() 이넘의 문제는 wait를 하고 있는 쓰레드가 있다면 깨워지지만 wait를 하고 있는 쓰레드가 없었다면... 아무것도 깨우지 않습니다. -_-;

그리고 WIN32는 쓰레드 핸들, 이벤트 핸들등.. 핸들이라는 이름을 갖는 모든 녀석을 한꺼번에 기다릴 수 있는 WaitForMultipleObjects() 라는 아주 멋진 API도 사용할 수 있지만 pthread는 여러개의 condition 개체를 한꺼번에 기다릴 수 있는 방법도 없고 개체의 종류(thread, condition)에 따라 다른 wait 펑션을 이용해야 합니다.

어째튼.. 해결법은...
이겁니다. 아래를 보시면 아시겠지만...

pthread_mutex_lock(..);
state = TRUE;
pthread_mutex_unlock(..);
pthread_cond_signal(..);

pthread_cond_signal()을 그냥 주시지 말고 state라는 변수를 하나 더 이용해야 합니다.

기다릴때는 state가 FALSE 일때 pthread_cond_wait()를 해 주시면 됩니다.

참고로 제가 사용하는 wait 펑션을 아래에 첨부하겠습니다.
저의 경우 한개의 소스코드로 Windows와 Linux에서 동시에 컴파일 되도록 개발하기때문에 좀 추상화 시켰습니다.

참고로
* wait_sec은 기다릴 시간
* manual_reset는 자동/수동 reset 기능은 갖습니다.

typedef struct
{
#ifdef STHREAD_USE_PTHREAD
	STHREAD_BOOL state;
	STHREAD_BOOL manual_reset;

	pthread_mutex_t mutex;
	pthread_cond_t cond;
#endif

#ifdef STHREAD_USE_WIN32
	HANDLE h_event;
#endif
}scond;

STHREAD_BOOL scond_create(scond* p_cond, STHREAD_BOOL manual_reset)
{
#ifdef STHREAD_USE_PTHREAD
	if(pthread_mutex_init(&(p_cond->mutex), NULL) != 0) return STHREAD_FALSE;

	if(pthread_cond_init(&(p_cond->cond), NULL) != 0)
	{
		pthread_mutex_destroy(&(p_cond->mutex));
		return STHREAD_FALSE;
	}

	p_cond->state = STHREAD_FALSE;
	p_cond->manual_reset = manual_reset;

	return STHREAD_TRUE;
#endif

#ifdef STHREAD_USE_WIN32
	return (p_cond->h_event = CreateEvent(NULL, manual_reset == STHREAD_TRUE ? TRUE : FALSE, FALSE, NULL)) == NULL ? STHREAD_FALSE : STHREAD_TRUE;
#endif
}

STHREAD_BOOL scond_set(scond* p_cond)
{
#ifdef STHREAD_USE_PTHREAD
	pthread_mutex_lock(&(p_cond->mutex));

	p_cond->state = STHREAD_TRUE;

	pthread_mutex_unlock(&(p_cond->mutex));

	pthread_cond_signal(&(p_cond->cond));

	return STHREAD_TRUE;
#endif

#ifdef STHREAD_USE_WIN32
	return SetEvent(p_cond->h_event) == TRUE ? STHREAD_TRUE : STHREAD_FALSE;
#endif
}

STHREAD_BOOL scond_reset(scond* p_cond)
{
#ifdef STHREAD_USE_PTHREAD
	pthread_mutex_lock(&(p_cond->mutex));

	p_cond->state = STHREAD_FALSE;

	pthread_mutex_unlock(&(p_cond->mutex));

	return STHREAD_TRUE;
#endif

#ifdef STHREAD_USE_WIN32
	return ResetEvent(p_cond->h_event) == TRUE ? STHREAD_TRUE : STHREAD_FALSE;
#endif
}

STHREAD_BOOL scond_wait(scond* p_cond, unsigned int wait_sec)
{
#ifdef STHREAD_USE_PTHREAD
	STHREAD_BOOL result;
	struct timeval now;
	struct timespec timeout;
	int retcode;

	pthread_mutex_lock(&(p_cond->mutex));

	switch(wait_sec)
	{
	case 0:
		result = p_cond->state;
		break;

	case STHREAD_INFINITE:
		while(p_cond->state == STHREAD_FALSE)
		{
			pthread_cond_wait(&(p_cond->cond), &(p_cond->mutex));
		}

		result = STHREAD_TRUE;
		break;

	default:
		if(gettimeofday(&now, NULL) == 0)
		{
			timeout.tv_sec = now.tv_sec + wait_sec;
			timeout.tv_nsec = now.tv_usec * 1000;

			retcode = 0;

			while(p_cond->state == STHREAD_FALSE && retcode != ETIMEDOUT)
			{
				retcode = pthread_cond_timedwait(&(p_cond->cond), &(p_cond->mutex), &timeout);
			}

			result = (retcode == ETIMEDOUT) ? STHREAD_FALSE : STHREAD_TRUE;
		}
		else
		{
			result = STHREAD_FALSE;
		}
	}

	if(result == STHREAD_TRUE)
	{
		if(p_cond->manual_reset == STHREAD_TRUE)
		{
			// 수동 리셋으로 설정되어 있다면 다른 wait 펑션이 동작할 수 있도록 해준다.
			pthread_cond_signal(&(p_cond->cond));
		}
		else
		{
			// 자동 리셋으로 설정되어 있다면 상태를 리셋해야 한다.
			p_cond->state = STHREAD_FALSE;
		}
	}

	pthread_mutex_unlock(&(p_cond->mutex));

	return result;
#endif

#ifdef STHREAD_USE_WIN32
	return WaitForSingleObject(p_cond->h_event, wait_sec * 1000) == WAIT_OBJECT_0 ? STHREAD_TRUE : STHREAD_FALSE;
#endif
}

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.