使用互斥锁还是不使用互斥锁?

5
如果我只有一个读者和一个写者,那么我是否需要互斥锁?读者从队列中获取下一个命令(food.front())并根据该命令执行任务。命令执行完毕后,它会弹出该命令。写者将命令推入队列(food.push())。
我需要互斥锁吗?我的读者(消费者)只有在 food.size() > 0 时才会执行。我正在使用读者线程和发送线程。

1
你在上面使用哪个容器? - Timo Geusch
@Timo:queue<unsigned char> food; - user195488
5个回答

11

互斥锁在多线程环境中使用。我没有在您的问题中看到提到线程的内容,因此我认为并不需要使用互斥锁。

但是,如果我们假设您所说的“读者”和“写作者”是指两个线程,则需要使用互斥锁(或其他多线程保护方案)来保护共享数据。

当队列有元素时,读取线程从中弹出某个元素,同时写入线程又向其中添加了元素会发生什么情况?灾难!使用互斥锁,您可以确保一次只有一个线程正在操作队列。

另一种方法是无锁线程安全队列。它使用原子操作来确保数据不被错误地操作。


1
@John:啊,我通常不注意标签,我的错。 - GManNickG
@GManNickG:什么是“无锁线程安全队列”?是根据我的需求构建的队列吗?还是STL中有这样的东西? - steffen
@steffen:我不建议自己构建一个,因为这很难做到正确。Intel的TBB和MSVC的PPL库都有一个名为“concurrent_queue”的队列。但是,如果您不需要无锁属性(这个属性通常只在高负载情况下有用),您可以很容易地实现一个阻塞并发队列:http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html - GManNickG

1

如果读者看到大小大于零,但结构尚未完全更新,会发生什么?

可以通过非常小心地编写更新来避免这种情况,但使代码抵御未来篡改的方法是使用互斥锁。


1
如果我们谈论在多处理器/多核架构上执行,"非常小心地编写更新"意味着在正确的位置使用正确的内存屏障,这是一个头疼的问题。 - Pascal Cuoq

1
假设“写入者”和“读取者”在不同的线程中:
很可能是的:在“写入”事件和“读取”事件之间,指向结构体的指针保持一致可能会出现“亚稳态”状态。
当然,这取决于实现方式:如果使用原子操作来更新指针,则可能无需互斥锁。

1

这完全取决于实现方式,如果您有两个不同的线程访问相同的变量,则需要使用互斥锁。否则,您可能会得到一个不一致的计数。

比如在写入时,您执行++count,在读取时执行--count,假设当前值为2。现在请注意,这些语句不需要是原子操作,++count可能由读取变量count、将其增加然后再次写回组成。现在,当一个写入和读取同时被执行,并且当执行第一个写入位时(即加载值2),整个读取已被执行,减少了计数,但是另一个线程仍然加载值2,它将其递增并随后写回变量。现在您刚刚失去了一个读取动作。


0

你的问题取决于两个条件:

  1. 只有两个线程,一个是生产者,另一个是消费者
  2. 该结构被设计为无锁

如果两个条件都满足,则可以放弃锁定,否则需要使用锁来保护队列结构。
要放弃锁定,必须记得在步骤结束时更新头部或尾部指针


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接