C++ 此代码中的竞争条件是什么?

C++ 此代码中的竞争条件是什么?,c++,c,multithreading,C++,C,Multithreading,下面是我对SleepConditionVariableCS、WakeAllConditionVariable和线程创建函数的实现。问题是,有时当我尝试创建线程时,创建线程会卡在WaitForSingleObject(cv->mut,INFINITE)上在SleepConditionVariableCS中。我搞不懂这里的比赛条件是什么 typedef struct { int waiters_count; HANDLE sema_; HANDLE mut; } CONDITION_V

下面是我对
SleepConditionVariableCS
WakeAllConditionVariable
和线程创建函数的实现。问题是,有时当我尝试创建线程时,创建线程会卡在
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
}

我看不出有什么逻辑上的缺陷对我来说很突出

然而

在使用条件变量之前,是否将其初始化为已知状态

您没有检查一些系统调用的返回,这些系统调用可能返回一个您永远看不到的错误
WaitForSingleObject
EnterCriticalSection
可能会失败或被赋予错误的句柄

在处理条件变量时,是否总是锁定
线程->锁定
,并保持锁定状态?我看到了两段不同的代码,它们引用了
thread->lock


您是否每次都以相同的顺序锁定/解锁关键部分和SEM?

我看不到任何真正突出的逻辑缺陷

然而

在使用条件变量之前,是否将其初始化为已知状态

您没有检查一些系统调用的返回,这些系统调用可能返回一个您永远看不到的错误
WaitForSingleObject
EnterCriticalSection
可能会失败或被赋予错误的句柄

在处理条件变量时,是否总是锁定
线程->锁定
,并保持锁定状态?我看到了两段不同的代码,它们引用了
thread->lock


您是否每次都以相同的顺序锁定/解锁关键部分和SEM?

在尝试使用win32 API实现pthread样式的条件变量时,请查看以下非常详细的问题分析。(特别是第3.4节)

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


在尝试使用win32 API实现pthread样式的条件变量时,请查看以下非常详细的gotchas分析。(特别是第3.4节)

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



SleepConditionVariableCS
中,首先调用
LeaveCriticalSection
,然后再调用
EnterCriticalSection
。smells@user931794我知道,但是我获得了一个不同的锁(
cv->mut
)来弥补这个缺口。这是必要的,因为我无法同时睡眠和解锁
关键部分。如果您知道错误具体在哪里,那将非常有用。这可能是一个愚蠢的问题,但是
thread->condition\u variable
在哪里初始化?@simonc in
KernelThread\u malloc()
mut是如何初始化的?在
SleepConditionVariableCS
中,首先调用
LeaveCriticalSection
,然后调用
EnterCriticalSection
第二次。smells@user931794我知道,但是我获得了一个不同的锁(
cv->mut
)来弥补这个缺口。这是必要的,因为我无法同时睡眠和解锁
关键部分。如果你明确知道错误在哪里,那将非常有帮助。可能是个愚蠢的问题,但是
thread->condition\u variable
在哪里初始化?@simonc in
KernelThread\u malloc()
mut是如何初始化的?我知道这些是最佳实践,但是我给出的代码本身导致了竞争条件:函数块等待已经过去的WakeAllConditionVariable信号。在该代码中,您可以看到线程锁被获取,并且在完成之前不会被释放。我正在努力添加更多的检查,但在检查后,
EnterCriticalSection
无效。不,只是在添加错误检查的情况下再次获得竞争条件,在
WaitForSingleObject
上均未报告任何错误。我知道这些是最佳做法,但是我给出的代码本身导致了竞争条件:函数块等待已经过去的WakeAllConditionVariable信号。在该代码中,您可以看到线程锁被获取,并且在完成之前不会被释放。我正在努力添加更多的检查,但是在检查之后,
EnterCriticalSection
是无效的。不,刚刚在添加错误检查的情况下再次获得竞争条件,在
WaitForSingleObject
上都没有报告错误。这是一个很好的观点,但是,你知道如何纠正这一点,而不把它全部扔掉,并从链接复制一个解决方案吗?在减量(使用非零检查)之前进入锁是否有效?这将解决数据争用问题。但随后你会遇到其他(不太可能和更微妙的)问题。例如,您超时,但在获取cv->mut之前,SleepConditionVariableCS()中的其他一些线程可能会首先获取cv->mut并增加cv->waiters\u计数。我不能完全理解这种情况,但我认为它不好。现在我想起来,这不会是错误,因为睡眠是用
INFINITE
调用的,因此返回值永远不会是
WAIT\u TIMEOUT
。啊,我现在明白了。(我没有注意到SleepConditionVariableCS()的第三个参数,因为它滚到了代码框的右侧!)您是否创建了任何窗口或调用了CoInitialize?执行上述任一操作的线程都不允许在无限超时的情况下调用WaitForSingleObject()。这是一个很好的观点,但你知道如何纠正这一点,而不把它全部扔掉,并从链接复制一个解决方案?在减量(使用非零检查)之前进入锁是否有效?这将解决数据争用问题。但随后你会遇到其他(不太可能和更微妙的)问题。例如,您超时,但在
    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()
    }