2011-04-28 75 views
3

你能发现代码中的错误吗?门票最终低于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循环将让第二个线程通过!

对不起,额外的问题。我以为我不明白窗口低级同步原语,但我真的只是有一个简单的错误。

+0

能否请您提供您的第二个代码示例的变量声明。 – Alerty 2011-04-28 06:40:45

+0

@ereOn会做,感谢提醒! – coderdave 2011-04-28 06:53:05

+0

@coderdave:Upvoted你的问题的良好行为:) – ereOn 2011-04-28 07:03:02

回答

0

我发布了一个错误,我认为这是一个合法的多线程问题,但实际上这只是错误的逻辑。我一发布就解决了这个错误。这是问题的线条和回答

unsigned int lockedaquired = 0; 

I初始化lockaquired为0,然后在我加入if语句来跳过做了CAS的昂贵的操作。这种优化导致它从while循环跳出并进入临界区。将代码更改为

unsigned int lockedaquired = 1; 

解决了问题。我发现代码中还有另一个隐藏的问题(我真的不应该在深夜编码)。任何人在关键部分的if语句后注意到分号?唉...

if(*ctxt->ticket > 0); 
    (*ctxt->ticket)--; 

这应该是

if(*ctxt->ticket > 0) 

此外,本杰克逊指出,一个线程可能会成为关键的部分内部时,我们将重置票eNumThreads。如果您将它应用于需要执行更多操作的问题,这在此示例代码中完全正常,但它可能不安全,因为线程没有以锁步方式运行,因此请记住,如果将此应用于您的码。

最后要说明的是,如果有人确定使用此代码来实现互斥锁,请记住您的主驱动程序线程正在空转。如果您在关键部分执行大型操作需要一定的时间并且您的票数很高,请考虑让您的线程让其他软件在等待时使用CPU。另外,如果临界区域很大,请考虑使用旋转锁定。

谢谢

1

我在您的代码中看到另一场比赛:一个线程可能导致*ctxt.ticket命中0,允许父环路返回并重新设置*ctxt.ticket = eNumThreads而不持有*ctxt.mutex。其他一些线程现在可能已经拥有该互斥锁(实际上它可能会),并在*ctxt.ticket上运行。对于你的简化示例,这只能防止“批次”被干净地分离,但如果你在loops循环的顶部有更复杂的初始化(比单个文字更复杂),你可能会看到奇怪的行为。

+0

是的,这是真的!在这个简化的例子中,虽然它很安全,但这不是一个复制和粘贴到实际代码的好模型。感谢您的好评。 – coderdave 2011-04-28 17:05:26