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 inKernelThread\u malloc()
mut是如何初始化的?在SleepConditionVariableCS
中,首先调用LeaveCriticalSection
,然后调用EnterCriticalSection
第二次。smells@user931794我知道,但是我获得了一个不同的锁(cv->mut
)来弥补这个缺口。这是必要的,因为我无法同时睡眠和解锁关键部分。如果你明确知道错误在哪里,那将非常有帮助。可能是个愚蠢的问题,但是thread->condition\u variable
在哪里初始化?@simonc inKernelThread\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()
}