指向STL Container Thread Safety(Queue / Deque)的指针

时间:2013-08-01 04:11:30

标签: c++ multithreading concurrency stl

我目前有一点多线程难题。我有两个线程,一个读取串行数据,另一个尝试从数据中提取数据包。这两个线程共享一个队列。尝试创建数据包的线程有一个名为parse的函数,其声明如下:

Parse(std::queue<uint8_t>* data, pthread_mutex_t* lock);

本质上它需要一个指向STL队列的指针,并在它通过队列寻找数据包时使用pop()。使用锁是因为任何pop()都被锁定,并且此锁在Parse函数和将数据推送到队列的线程之间共享。这样,可以在数据被主动添加到队列时解析队列。

代码似乎在大多数情况下工作,但我看到的无效数据包的速度比我预期的要高一些。我的主要问题是我想知道当我从队列中读取数据时指针是否正在改变。例如,如果第一个线程推送了一堆数据,那么在内存中找到队列的位置是否有可能发生变化?或者我保证指向队列的指针将保持不变,即使添加了数据?我担心的是队列的内存可以在我的Parse()函数中重新分配,因此在我的函数中间,指针无效。

例如,我了解某些STL迭代器对某些操作无效。但是,我传递一个指向容器本身的指针。就是这样:

// somewhere in my code I create a queue
std::queue<uint8_t> queue;
// elsewhere...
Parse(&queue, &lock_shared_between_the_two_threads);

指向容器本身的指针是否会失效?它指向什么?第一个元素,或者......?

请注意,我没有指向任何给定元素,而是指向容器本身。此外,我从未指定应该使用哪个底层容器来实现队列,所以在它下面,它只是一个双端队列。

非常感谢任何帮助。

编辑8/1:

我能够对我的代码运行一些测试。几点:

  1. 容器本身的指针在程序的生命周期内不会改变。这是有道理的,因为队列本身是类的成员变量。也就是说,虽然队列的元素是动态分配的,但队列本身似乎并非如此。

  2. 我遇到的坏数据包似乎是我收到的串行数据的函数。我将所有数据转储到hex文件中,并且能够找到无效的数据包,并且我的算法正确地标记了它们。

  3. 因此,我认为将一个引用或指向STL容器的指针传递给一个函数是线程安全的,但是我想听一些更多的评论来确保这种情况,或者这是实现特定的(很多STL是......)。

1 个答案:

答案 0 :(得分:2)

您担心在一个线程中修改容器(添加/删除节点)会以某种方式使指向另一个线程中容器的指针无效。容器是一个抽象,除非您删除容器对象本身,否则它将保持有效。容器维护的数据的内存通常由stl::allocators在堆上分配。

这与为容器对象本身分配的内存完全不同,容器对象本身可以在堆栈,堆等上,基于容器对象本身的创建方式。 containerallocator的这种分离是防止对数据进行某些修改以修改容器对象本身的原因。

为了简化调试问题,就像Jonathan Reinhart建议的那样,让它成为一个单线程系统,读取流并解析它。

另外,您是否考虑过使用Boost Lookfree Queues或类似内容。它们专为此类场景而设计。如果您经常接收数据包/读取它们,则锁定队列以便为每个数据包进行读/写操作可能会成为显着的性能开销。