2010-12-08 3 views
1

I (내 원래 코드에서 적응) 다음과 같은 소스 코드를했습니다 :pthread_mutex_unlock을하지 원자

#include "stdafx.h" 
#include <stdlib.h> 
#include <stdio.h> 

#include "pthread.h" 

#define MAX_ENTRY_COUNT 4 

int entries = 0; 
bool start = false; 

bool send_active = false; 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t condNotEmpty = PTHREAD_COND_INITIALIZER; 
pthread_cond_t condNotFull = PTHREAD_COND_INITIALIZER; 

void send() 
{ 
    for (;;) { 
     if (!start) 
      continue; 
     start = false; 

     for(int i = 0; i < 11; ++i) { 
      send_active = true; 

      pthread_mutex_lock(&mutex); 
      while(entries == MAX_ENTRY_COUNT) 
       pthread_cond_wait(&condNotFull, &mutex);  
      entries++; 
      pthread_cond_broadcast(&condNotEmpty); 
      pthread_mutex_unlock(&mutex); 

      send_active = false; 
     } 
    } 
} 

void receive(){ 
    for(int i = 0; i < 11; ++i){ 
     pthread_mutex_lock(&mutex); 
     while(entries == 0) 
      pthread_cond_wait(&condNotEmpty, &mutex); 
     entries--; 
     pthread_cond_broadcast(&condNotFull); 
     pthread_mutex_unlock(&mutex); 
    } 

    if (send_active) 
     printf("x"); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    pthread_t s; 

    pthread_create(&s, NULL, (void *(*)(void*))send, NULL); 

    for (;;) { 
     pthread_mutex_init(&mutex, NULL); 
     pthread_cond_init(&condNotEmpty, NULL); 
     pthread_cond_init(&condNotFull, NULL); 

     start = true; 

     receive(); 

     pthread_mutex_destroy(&mutex); 
     mutex = NULL; 
     pthread_cond_destroy(&condNotEmpty); 
     pthread_cond_destroy(&condNotFull); 
     condNotEmpty = NULL; 
     condNotFull = NULL; 

     printf("."); 
    } 

    return 0; 
} 

다음과 같은 문제는 : 시간에서 송신 기능의 마지막 잠금 해제가되기 전에 완료되지 않습니다 시간 수신 방법이 계속됩니다. 원래 코드에서 뮤텍스는 작업을 수행 한 후에 삭제되는 객체에 있습니다. send 메소드가 마지막 잠금 해제로 끝나지 않았다면 뮤텍스는 유효하지 않으며 내 프로그램은 잠금 해제시 실패를 유발합니다.

프로그램을 실행하면 문제를 쉽게 재현 할 수 있습니다. "x"가 표시 될 때마다 수신 방법이 거의 완료되고 보내기 메서드가 잠금 해제 호출에 "중단"됩니다.

VS2008 및 VS2010으로 컴파일했습니다. 두 결과가 동일합니다.

pthread_mutex_unlock은 원자가 아니므로 문제가 해결됩니다. 이 문제를 어떻게 해결할 수 있습니까? 모든 의견은

안부

마이클

+0

나는이 문제를 완전히 이해하고 있는지 확신 할 수는 없지만 의존하는 뮤텍스를 파괴하기 전에 send 스레드가 종료 될 때까지 기다릴 필요는 없다 ('pthread_join (s, NULL);') ? –

+0

또한 정적 초기 화기'PTHREAD_MUTEX_INITIALIZER'와'pthread_mutex_init' 호출로 뮤텍스를 초기화 할 필요가 없습니다. (이미 초기화 된 뮤텍스를 초기화하려고하면 정의되지 않은 동작이 발생합니다.) –

+0

보내기 스레드는 내 응용 프로그램에서 끝나지 않는 스레드입니다 (사실상 I/O 스레드입니다). – michael

답변

1

귀하의 printf ("X") 교과서 경쟁 조건의 예입니다 ... 환영합니다.

pthread_mutex_unlock() 이후 OS는 임의의 시간 동안이 스레드를 예약하지 않아도됩니다. 틱, 초 또는 일. send_active가 제 시간에 "위조"될 것이라고 생각할 수 없습니다.

+0

인쇄 경쟁 상태가 명확합니다. 하지만 : 내 send 함수의 내부 for 루프에서 마지막으로 잠금 해제가되면 receive 함수가 계속되기 전에 가끔 반환되지 않습니다. 그게 내 문제 야. – michael

+0

@ 마이클 :'send_active'에서 경쟁 조건을 쫓지 않았다면 왜 모드가 문제인지 명확하게 보여주는 코드를 보여줄 수 있습니까? –

+0

@michael pthread_mutex_unlock()이 잠금 해제 직후 실제로 복귀한다는 보장은 없습니다. 분명히 "return"문과 스레드 스케줄링이 syscall return에서 발생하기 전에 내부에 일부 syscall이 있습니다. 더 높은 우선 순위의 쓰레드/프로세스가 CPU 시간을 필요로한다면, 쓰레드는 커널에 영원히 걸릴 수 있습니다. CALL 이후에 커널에 머물렀다가 하나의 명령이 생기는 것의 차이를 거의 알 수 없습니다. pthread_mutex_unlock() – blaze

1

pthread_mutex_unlock() 뮤텍스가 반환되기 전에 정의에 의해 뮤텍스를 릴리스해야합니다. 뮤텍스가 해제 된 순간에 뮤텍스와 경쟁하려는 다른 스레드를 예약 할 수 있습니다. pthread_mutex_unlock()이 리턴 된 직후까지 뮤텍스를 해제하지 않아도 (네가 원자 단위라고 생각하는 것), 현재 볼 수있는 것과 동일한 경쟁 조건이 존재할 것이다 (나에게 명확하지 않다. 당신이보고있는 어떤 경쟁은 printf() 콜을 제어하기 위해 send_active에 액세스하는 경쟁 조건에 정말로 관심이 없다는 것을 보여주기 때문에 볼 수 있습니다.

그런 경우 다른 스레드는 pthread_mutex_unlock()의 'between-the-lines'과 그 함수를 호출 한 다음 명령문/표현식으로 예약 할 수 있습니다. 동일한 경쟁 조건이 있습니다.

+0

디버거로 코드를 검사했고 사실 pthread_mutex_unlock이 반환되지 않았습니다. 스레드가 pthread_mutex_unlock의 SetEvent 호출에서 중지되었습니다. – michael

+0

send_active 및 printf는 디버깅을위한 헬퍼입니다. printf에 중단 점을 설정하면 pthread_mutex_unlock에 SetEvent 행이 표시됩니다. – michael

+0

제가보기에 - pthread 라이브러리의 버그처럼 보입니다 (R ..이 주석에 언급 된 것처럼). 어떤 pthread 라이브러리를 사용하고 있습니까? –

0

여기에 어떤 일이 벌어지고 있는지에 대한 추측이 있습니다. 이 분석에 대한 몇 가지주의 사항 :

  • 이것은 당신이 http://sourceware.org/pthreads-win32/
  • 에서의 Win32의 pthreads 패키지를 사용하는 가정을 기반으로이 해당 사이트에서의 pthreads 소스의 매우 간단한 검사를 기반으로하며 여기에있는 질문과 의견의 정보 - 실제로 코드를 실행/디버깅 할 기회가 없었습니다. pthread_mutex_unlock()가 호출

, 그것은 잠금 수를 감소하고, 그 잠금 횟수가 0으로 떨어지면은 Win32 SetEvent() API는 차단을 해제 할 뮤텍스를 기다리고있는 스레드 수 있도록 관련 이벤트 객체에서 호출됩니다. 꽤 표준적인 것들.

여기서 추측이 이루어집니다. SetEvent()이 뮤텍스를 기다리는 스레드의 차단을 해제하기 위해 호출되었으며, 주어진 핸들과 관련된 이벤트를 알립니다.그러나 SetEvent() 함수가 다른 작업을 수행하기 전에 다른 스레드가 실행을 시작하고 해당 SetEvent()이 호출 된 이벤트 개체 핸들을 닫습니다 (pthread_mutex_destroy() 호출).

이제는 진행중인 SetEvent() 호출이 더 이상 유효하지 않은 핸들 값을 갖습니다. 나는 특별한 이유를 생각할 수 없다. 이벤트를 알리는 신호를받은 후에 SetEvent()이 그 핸들을 사용하면 아무 것도 할 수 없다. (아마도 나는 또한 누군가가 상상할 수있는 적당한 인수를 상상할 수도있다. SetEvent()은 이벤트 객체 핸들이 남아있을 것이라고 기대할 수 있어야한다. API 호출 기간 동안 유효 함).

이런 일이 생기면 이 큰 경우 인 경우 쉽게 해결할 수 있을지 잘 모르겠습니다. 나는 pthreads 라이브러리가 SetEvent()을 호출하기 전에 이벤트 핸들을 복제하도록 변경해야한다고 생각하고 SetEvent() 호출이 반환 될 때 복제본을 닫는다. 그런 식으로 'main'핸들이 다른 스레드에 의해 닫히더라도 핸들은 유효하게 유지됩니다. 나는 그것을 여러 장소에서해야 할 것이라고 추측하고있다. 이 문제는 영향을받는 Win32 API 호출을 "중복 처리/호출 API/닫기 복제"시퀀스를 수행하는 래퍼 함수 호출로 대체하여 구현할 수 있습니다.

SetEvent() 전화를 pthread_mutex_unlock()으로 변경하고 특정 문제를 해결했는지 확인하는 것이 바람직하지 않을 수 있습니다. 그렇다면 라이브러리의 유지 관리자에게 연락하여 좀 더 포괄적 인 수정 프로그램을 어떻게 든 준비 할 수 있는지 확인하십시오 (준비 - 작업의 상당 부분을 수행하도록 요청받을 수 있음).

호기심에서 벗어난 pthread_mutex_unlock()/SetEvent()에 걸려있는 스레드 상태를 디버깅하는 중에 발생하는 문제에 대한 정보가 있습니까? SetEvent()가 무엇을 기다리고 있습니까? (Windows 용 디버깅 도구 패키지에있는 cdb 디버거는 Visual Studio 디버거보다 더 많은 정보를 제공 할 수 있습니다).

는 또한, 관련 보인다 pthread_mutex_destroy() (그러나 다른) 특정 문제보다는의 소스에 다음과 같은 코멘트를주의 :

/* 
* FIXME!!! 
* The mutex isn't held by another thread but we could still 
* be too late invalidating the mutex below since another thread 
* may already have entered mutex_lock and the check for a valid 
* *mutex != NULL. 
* 
* Note that this would be an unusual situation because it is not 
* common that mutexes are destroyed while they are still in 
* use by other threads. 
*/ 
0

마이클, 당신의 조사 및 의견 주셔서 감사합니다!

코드는 http://sourceware.org/pthreads-win32/입니다.

세 번째와 네 번째 단락에 나와있는 상황이 정확히 무엇이 일어나고 있는지를 보여줍니다.

몇 가지 해결책을 살펴 보았습니다. 간단한 해결책은 나를 위해 작동하는 것 같습니다. send 함수 (및 SetEvent)가 완료 될 때까지 기다렸습니다. 이 솔루션으로 수행 한 모든 테스트는 지금까지 성공적이었습니다. 나는 주말에 더 큰 시험을 할 것입니다.