2010-10-27 242 views
18

我正在使用队列在线程之间进行通信。我有一个阅读器和多个写入器线程。我的问题是,当我使用阅读器队列中的push/front/pop时,是否需要锁定队列?我可以这样做以下:STL队列的线程安全

//reader threads 
getLock(); 
get the number of elements from the queue 
releaseLock(); 

int i = 0; 
while(i < numOfElements){ 
    queue.front(); 
    queue.pop(); 
    i++ 
} 

的想法是,我想减少锁定代码和自写线程只会写入队列后面的粒度,并且只有一个单一的阅读器线。只要我得到元素的数量,那么我可以从队列中获取元素,或者我是否需要将front()pop()也包含在锁中?

回答

8

任何类型的没有明确说明它的线程安全保证应始终由一个互斥体来控制。也就是说,你的实现的stdlib可能会允许一些变化 - 但你不能知道所有std :: queue的实现。

由于性病::队列包装另一个容器(这是一个容器适配器),你需要看看下面的容器,默认为双端队列。

您可能会发现更容易,更好,更便携编写自己的容器适配器,让你需要的保证。我不知道这些事情是否适用于Boost中的队列。

我没有看过的C++ 0x足以知道其是否具有该出的现成的任何解决方案,但可能是另一种选择。

+4

的C++ 0x具有原子公司提供的情况和标准执行线程安全的,在C无锁队列,所以它使程序员能够无锁的一个很好的解释(线程安全)算法,并提供标准互斥锁,但它没有任何开箱即用的功能。 – GManNickG 2010-10-27 02:32:27

2

这完全是依赖于实现的。 C++标准提到了线程或线程安全性,因此这是否会起作用取决于您的实现如何处理队列元素。

在你的情况,读者实际上是弹出队列,这被认为是一个写操作。我怀疑在这种情况下,当多个线程同时写入一个容器时,任何常见的实现实际上都能保证线程安全。至少VC++没有:

对于读取到同一个对象,当其他线程上没有写入者时,该对象是线程安全的以供读取。

对于写入同一个对象,该对象对于在其他线程上没有读取器时从一个线程写入的线程安全。

9

正如其他人已经提到的那样,标准容器不需要保证线程安全,所以你要求的东西不能被移植实现。通过使用2个队列和一个队列指针,可以减少读者线程锁定写入者的时间,该队列指针指示写入者当前正在使用的队列。

每个写入器将:

  • 采集锁
  • 推元件(一个或多个)到队列中当前由队列指针指向
  • 推出锁

读者然后可以做如下:

  • Acquir e锁定
  • 切换队列指针从所述第一队列指向所述第二队列
  • 推出锁
  • 过程元件
+0

作者将如何工作? – GManNickG 2010-10-27 02:27:22

+2

作者总是追加到队列指针当前指向的任何队列(在第一次获取锁之后)。在这种情况下,锁正在保护指针当前引用的任何队列(和指针本身);另一个队列(“第一队列”)可以由没有锁的读卡器处理。 – 2010-10-27 02:47:21

1

有时,可以通过避免之间共享状态或资源解决了很多并发头痛的线程。如果你有多个线程同时访问一个容器以推进他们的工作,然后尝试让他们在专用容器上工作。在特定的时间点,您将以不同时的方式将容器的元素收集到中央容器上。

如果您可以避免在线程间共享状态或资源,那么同时运行线程并没有问题。线程不需要担心对方,因为它们是完全隔离的,彼此不起任何作用。

1

你的预感是正确的:即使你不能指望STD队列是线程安全的,队列应该是线程安全的设计

的这是为什么++由van Dooren

+2

对于特殊情况下的单一生产者,单个消费者以及关于编译器生成的代码中的内存屏障和原子读/写的一些更多假设,天真的锁免费队列实现仅部分“线程安全”。然而,实现真正的锁定空闲线程安全队列是可能的,但并不那么简单。 – Leo 2015-07-25 01:00:08