2013-10-09 104 views
2

wikipedia中提及的生产者消费者问题的“执行不力”的伪代码如下。据说这种解决方案具有可能导致死锁的竞态条件。多线程编程 - 生产者消费者

我的问题是:不只是修改唤醒其他线程的条件如下解决了可能的死锁问题。这样就不会有一个可能会丢失的唤醒,但随后会有多个唤醒,或者我错过了一些东西。尝试在这里了解。

int itemCount = 0; 

procedure producer() { 
    while (true) { 
     item = produceItem(); 

     if (itemCount == BUFFER_SIZE) { 
      sleep(); 
     } 

     putItemIntoBuffer(item); 
     itemCount = itemCount + 1; 

     //if (itemCount == 1) <<<<<<<< change this to below condition 
     if(itemCount > 0) 
     { 
      wakeup(consumer); 
     } 
    } 
} 

procedure consumer() { 
    while (true) { 

     if (itemCount == 0) { 
      sleep(); 
     } 

     item = removeItemFromBuffer(); 
     itemCount = itemCount - 1; 

     //if (itemCount == BUFFER_SIZE - 1) <<<<<<< Change this to below 
     if(itermCount < BUFFER_SIZE) 
     { 
      wakeup(producer); 
     } 

     consumeItem(item); 
    } 
} 

回答

1

将不仅仅改变了wakeing其他线程如下解决可能的死锁问题的条件。

不,竞态条件依然存在。问题是有多个线程正在进行消费和/或生产。当消费者(例如)被唤醒并被告知有待处理的项目时,它可能会删除该项目,但其他一些线程(或多个线程)已经到达那里。

解决的办法是做到以下几点:

lock() { 
    while (itemCount == 0) { 
     sleep(); 
    } 

    item = removeItemFromBuffer(); 
    itemCount = itemCount - 1; 
} 

因此,即使消费者被唤醒,它会立即再次检查itemCount不为0与while循环。即使itemCount增加了,另一个线程可能已经删除了该元素,并在之前递减itemCount,得到该信号的线程有机会采取行动。这就是比赛。

生产者方面也是如此,虽然种族是阻止生产者过度填充缓冲区。生产者可能会因为有可用空间而被唤醒,但到了将物品放入缓冲区的时候,其他线程会打败它并重新填充缓冲区。在它被唤醒之后,它必须再次测试以确保有空间。

我从本网站的网站上一行一行的详细说明了这场比赛,标题为Producer Consumer Thread Race Conditions。这里还有一个小测试程序来演示这个问题。

要实现的重要一点是,在大多数锁定实现中,有一个线程队列等待访问锁。当一个信号发送到一个线程时,它首先必须重新获取的锁,之前的它可以继续。当一个线程被发信号时,它将进入BLOCK队列的。如果有额外的线程正在等待锁定,但是不是正在等待,它们将在唤醒线程之前运行并窃取项目。

这与关于类似代码中的while循环的这个问题非常相似。不幸的是,接受的答案并不能解决这种竞争条件。请考虑加强my answer to a similar question here。虚假唤醒的一个问题,但这里真正的问题是维基百科讨论的竞争条件。

+1

也修改'itemCount'的所有行都需要是原子的或锁定的。 – Adam

+0

刚修复@Adam。谢谢。 – Gray

+0

在多个消费者生产者的情况下不应该将唤醒发送到所有线程?那样可以避免比赛? – goldenmean