您可以在代码中发现错误吗?门票最终跌至0以下,导致长时间的停滞。
struct SContext {
volatile unsigned long* mutex;
volatile long* ticket;
volatile bool* done;
};
static unsigned int MyThreadFunc(SContext* ctxt) {
// -- keep going until we signal for thread to close
while(*ctxt->done == false) {
while(*ctxt->ticket) { // while we have tickets waiting
unsigned int lockedaquired = 0;
do {
if(*ctxt->mutex == 0) { // only try if someone doesn't have mutex locked
// -- if the compare and swap doesn't work then the function returns
// -- the value it expects
lockedaquired = InterlockedCompareExchange(ctxt->mutex, 1, 0);
}
} while(lockedaquired != 0); // loop while we didn't aquire lock
// -- enter critical section
// -- grab a ticket
if(*ctxt->ticket > 0);
(*ctxt->ticket)--;
// -- exit critical section
*ctxt->mutex = 0; // release lock
}
}
return 0;
}
调用函数等待线程完成
for(unsigned int loops = 0; loops < eLoopCount; ++loops) {
*ctxt.ticket = eNumThreads; // let the threads start!
// -- wait for threads to finish
while(*ctxt.ticket != 0)
;
}
done = true;
编辑:
这个问题的答案很简单,很不幸,在我花了一些时间精简示例以发布简化版本之后,我在发布问题后立即找到了答案。叹..
我将lockaquired初始化为0。然后作为不占用总线带宽的优化,如果采用了互斥锁,则不执行CAS。
不幸的是,在这种情况下,使用了while循环的锁将使第二个线程通过!
抱歉,还有其他问题。我以为我不了解Windows低级同步原语,但实际上我只是犯了一个简单的错误。
最佳答案
我在您的代码中看到了另一场竞赛:一个线程可能导致*ctxt.ticket
达到0,从而允许父循环返回并重新设置*ctxt.ticket = eNumThreads
而不持有*ctxt.mutex
。现在,其他一些线程可能已经持有互斥体(实际上,它可能确实存在)并在*ctxt.ticket
上进行操作。对于您的简化示例,这只能防止“批”被完全分开,但是如果您在loops
循环的顶部进行了更复杂的初始化(例如,比单个单词写更为复杂),则会看到奇怪的行为。