您可以在代码中发现错误吗?门票最终跌至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循环的顶部进行了更复杂的初始化(例如,比单个单词写更为复杂),则会看到奇怪的行为。

09-12 09:28