2

维基百科中提到的生产者消费者问题的“实施不足”的伪代码如下。据说该解决方案具有可能导致死锁的竞争条件。

我的问题是:不只是修改唤醒另一个线程的条件如下解决可能的死锁问题。这样,不仅有一个可能会丢失的唤醒,而是后续的多个唤醒,或者我错过了什么。试图理解这里。

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);
    }
}
4

1 回答 1

1

不只是修改唤醒另一个线程的条件如下解决可能的死锁问题。

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

解决方案是执行以下操作:

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

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

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

生产者方面也是如此,尽管竞赛是为了阻止生产者过度填充缓冲区。生产者可能会因为有可用空间而被唤醒,但是当它将项目放入缓冲区时,其他线程已经击败它并重新填充缓冲区。它必须再次测试以确保它被唤醒后有空间。

我从我的名为Producer Consumer Thread Race Conditions的网站上逐行详细介绍了此页面上的这场比赛。那里还有一个小测试程序可以演示该问题。

要意识到的重要一点是,在大多数锁定实现中,都有一个线程队列等待获得对锁的访问权。当一个信号被发送到一个线程时,它首先必须重新获得锁,然后才能继续。当一个线程收到信号时,它会进入 BLOCK 队列的末尾。如果有额外的线程正在等待锁但没有等待,它们将在被唤醒的线程之前运行并窃取项目。

这与关于while类似代码中的循环的问题非常相似。不幸的是,接受的答案没有解决这种竞争条件。请考虑在此处投票支持我对类似问题的回答。虚假唤醒一个问题,但这里真正的问题是维基百科正在谈论的竞争条件。

于 2013-10-09T21:28:55.623 回答