2013-04-08 49 views
1

以下是我执行的SleepConditionVariableCSWakeAllConditionVariable和线程创建函数。问题是,有时当我尝试创建线程时,创建线程卡在WaitForSingleObject(cv->mut, INFINITE);SleepConditionVariableCS中。我无法弄清楚这里的竞赛状况。这段代码中的竞态条件是什么?


typedef struct 
{ 
    int waiters_count; 
    HANDLE sema_; 
    HANDLE mut; 
} CONDITION_VARIABLE; 

void SleepConditionVariableCS(CONDITION_VARIABLE *cv, CRITICAL_SECTION *cs, int32_t dwMilliseconds){ 
    WaitForSingleObject(cv->mut, INFINITE); //Acuire object lock 

    cv->waiters_count++; 
    LeaveCriticalSection (cs); 
    if (SignalObjectAndWait(cv->mut, cv->sema_, dwMilliseconds, FALSE) == WAIT_TIMEOUT){ //SignalObjectAndWait releases the lock 
     cv->waiters_count--; 
    } 
    EnterCriticalSection(cs); 
} 

void WakeAllConditionVariable(CONDITION_VARIABLE *cv){ 
    WaitForSingleObject(cv->mut, INFINITE); 

    while (cv->waiters_count > 0){ 
     cv->waiters_count = cv->waiters_count - 1; 
     ReleaseSemaphore (cv->sema_, 1, 0); 
    } 
    ReleaseMutex(cv->mut); 
} 

void KernelThread_CreationWait(void *kthread){ 
    KernelThread *thread = (KernelThread *) kthread; 

    EnterCriticalSection(thread->lock); 
    thread->state = Thread_CREATED; 

    WakeAllConditionVariable(thread->condition_variable); 

    LeaveCriticalSection(thread->lock); 
    KernelThread_main(kthread); 
} 

KernelThread* createKernelThread(){ 
    EventHandler_getHandler(); 

    unsigned long threadid; 
    int t; 
    void *hand; 

    KernelThread *thread = KernelThread_malloc(); 
    EnterCriticalSection(thread->lock); 
    thread->state = Thread_WAITINGFORCREATION; 
    hand = CreateThread(NULL, 
      0, // security, stack size 
      (LPTHREAD_START_ROUTINE)&KernelThread_CreationWait, // start 
      (void *)thread, 
      0, 
      &threadid); // param, creation flags, id 
    if (hand == NULL){ 
     printf("ERROR: return handle from CreateThread() is NULL\n"); 
     exit(-1); 
    } 
    thread->thread = hand; 
    thread->thread_id = threadid; 

    SleepConditionVariableCS(thread->condition_variable,thread->lock,INFINITE); 
    LeaveCriticalSection(thread->lock); 

    return thread; 
} 

void InitializeConditionVariable (CONDITION_VARIABLE *cv){ 
    cv->waiters_count = 0; 
    cv->sema_ = CreateSemaphore (NULL,  // no security 
           0,   // initially 0 
           0x7fffffff, // max count 
           NULL);  // unnamed 
    cv->mut = CreateMutex(
      NULL,    // default security attributes 
      FALSE,    // initially not owned 
      NULL);    // unnamed mutex 
} 
+0

'''SleepConditionVariableCS'''你叫'''LeaveCriticalSection'''先'''EnterCriticalSection'''第二。气味 – 2013-04-08 21:12:37

+0

@ user931794我知道,但我获得了一个不同的锁('cv-> mut')以弥补差距。这是必要的,因为我不能同时睡觉和解锁'CRITICAL_SECTION'。如果您明确知道错误的位置,那将会非常有帮助。 – chacham15 2013-04-08 21:15:57

+0

可能是一个愚蠢的问题,但是'thread-> condition_variable'被初始化了吗? – simonc 2013-04-08 21:17:26

回答

1

在尝试使用win32 apis实现pthread样式条件变量时,请查看以下详细分析。 http://www.cs.wustl.edu/~schmidt/win32-cv-1.html。 (尤其是第3.4节)。

在你的代码中,我目前只看到一个问题。在SleepConditionVariableCS()

LeaveCriticalSection (cs); 
    if (SignalObjectAndWait(cv->mut, cv->sema_, dwMilliseconds, FALSE) == WAIT_TIMEOUT){ //SignalObjectAndWait releases the lock 
     // RIGHT HERE YOU HAVE NOT ACQUIRED cv->mut 
     cv->waiters_count--; // SO THIS IS A DATA RACE (with the cv->waiters_count uses in WakeAllConditionVariable() 
    } 
+0

这是一个很好的观点,但是您是否知道如何纠正这一问题而不把它全部抛出并复制链接中的解决方案?在递减之前(使用非零检查)是否会输入锁定? – chacham15 2013-04-08 21:58:44

+0

这会照顾数据竞赛。但是,你会遇到其他(不太可能和更微妙的)问题。例如,你超时,但在获得cv-> mut之前,SleepConditionVariableCS()中的其他线程可能首先获取cv-> mut,然后增加cv-> waiters_count。在这种情况下,我不能完全理解,但我认为这并不好。 – 2013-04-08 22:10:52

+0

现在我想到了,这个错误是错误的,因为睡眠是通过'INFINITE'调用的,因此返回值永远不会是'WAIT_TIMEOUT'。 – chacham15 2013-04-08 22:11:25

0

我没有看到真正突出的逻辑缺陷。

但是......

你初始化CONDITION_VARIABLE到已知状态使用它之前?

你不检查几个系统调用的返回值,这可能会返回一个你永远不会看到的错误。 WaitForSingleObjectEnterCriticalSection可以失败或被给予错误的句柄。

你是否总是锁定thread->lock并在处理条件变量时保持锁定状态?我看到2个不同的代码片段,并参考了thread->lock

您是否每次都以相同的顺序锁定/解锁临界区域和区域?

+0

我明白这些都是最佳实践,但是我给自己的代码导致了竞争条件:'createKernelThread'功能块等待已经过去的'WakeAllConditionVariable'信号。在该代码中,您可以看到线程锁被获取并且在完成之前不会被释放。我正在努力增加一些检查,但是在检查EnterCriticalSection后是无效的。 – chacham15 2013-04-08 21:25:17

+0

不,刚刚得到竞争条件并添加了错误检查,没有任何错误报告在“WaitForSingleObject”上。 – chacham15 2013-04-08 21:47:50