简体   繁体   English

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

[英]Pointer to STL Container Thread Safety (Queue/Deque)

I currently have a bit of a multi-threading conundrum. 我目前有一个多线程难题。 I have two threads, one that reads serial data, and another that attempts to extracts packets from the data. 我有两个线程,一个读取串行数据,另一个尝试从数据中提取数据包。 The two threads share a queue. 这两个线程共享一个队列。 The thread that attempts to create packets has a function entitled parse with the following declaration: 尝试创建数据包的线程有一个名为parse的函数,其声明如下:

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

Essentially it takes a pointer to the STL queue and uses pop() as it goes through the queue looking for a packet. 本质上,它需要一个指向STL队列的指针,并在它通过队列寻找数据包时使用pop()。 The lock is used since any pop() is locked and this lock is shared between the Parse function and the thread that is pushing data onto the queue. 使用锁是因为任何pop()都被锁定,并且此锁在Parse函数和将数据推送到队列的线程之间共享。 This way, the queue can be parsed while data is being actively added to it. 这样,可以在数据被主动添加到队列时解析队列。

The code seems to work for the most part, but I'm seeing invalid packets at a somewhat higher rate than I'd expect. 代码似乎在很大程度上起作用,但我看到的无效数据包的速率比我预期的要高一些。 My main question is I'm wondering if the pointer is changing while I'm reading data out of the queue. 我的主要问题是我想知道当我从队列中读取数据时指针是否正在改变。 For example, if the first thread pushes a bunch of data, is there a chance that where the queue is found in memory can change? 例如,如果第一个线程推送了一堆数据,那么在内存中找到队列的位置是否有可能发生变化? Or am I guaranteed that the pointer to the queue will remain constant, even as data is added? 或者我保证指向队列的指针将保持不变,即使添加了数据? My concern is that the memory for the queue can be reallocated during my Parse() function, and therefore in the middle of my function, the pointer is invalidated. 我担心的是队列的内存可以在我的Parse()函数中重新分配,因此在我的函数中间,指针无效。

For example, I understand that certain STL iterators are invalidated for certain operations. 例如,我理解某些STL迭代器对某些操作无效。 However, I am passing a pointer to the container itself. 但是,我传递一个指向容器本身的指针。 That is, something like this: 就是这样的:

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

Does the pointer to the container itself ever get invalidated? 指向容器本身的指针是否会失效? And what does it point to? 它指向什么? The first element, or ...? 第一个元素,还是......?

Note that I'm not pointing to any given element, but to the container itself. 请注意,我没有指向任何给定的元素,而是指向容器本身。 Also, I never specified which underlying container should be used to implement the queue, so underneath it all, it's just a deque. 另外,我从未指定应该使用哪个底层容器来实现队列,所以在它下面,它只是一个双端队列。

Any help will be greatly appreciated. 任何帮助将不胜感激。

EDIT 8/1: 编辑8/1:

I was able to run a few tests on my code. 我能够对我的代码运行一些测试。 A couple of points: 几点:

  1. The pointer for the container itself does not change over the lifecycle of my program. 容器本身的指针在程序的生命周期内不会改变。 This makes sense since the queue itself is a member variable of a class. 这是有道理的,因为队列本身是类的成员变量。 That is, while the queue's elements are dynamically allocated, it does not appear to be the case for the queue itself. 也就是说,虽然队列的元素是动态分配的,但队列本身似乎并非如此。

  2. The bad packets I was experiencing appear to be a function of the serial data I'm receiving. 我遇到的坏包似乎是我收到的串行数据的函数。 I dumped all the data to a hex file and was able to find packets that were invalid, and my alogrithm was correctly marking them as such. 我将所有数据转储到hex文件,并且能够找到无效的数据包,并且我的算法正确地标记了它们。

As a result, I'm thinking that passing a reference or pointer to an STL container into a function is thread safe, but I'd like to hear some more commentary ensuring that this is the case, or if this is implementation specific (as alot of STL is...). 因此,我认为将一个引用或指向STL容器的指针传递给一个函数是线程安全的,但是我想听一些更多的评论来确保这种情况,或者这是否是特定于实现的(如很多STL是...)。

You are worried that modifying a container (adding/deleting nodes) in one thread will somehow invalidate the pointer to the container in another thread. 您担心在一个线程中修改容器(添加/删除节点)会以某种方式使指向另一个线程中容器的指针无效。 The container is an abstraction and will remain valid unless you delete the container object itself. 容器是一个抽象,除非您删除容器对象本身,否则它将保持有效。 The memory for the data maintained by the containers are typically allocated on the heap by stl::allocators . 容器维护的数据的内存通常由stl::allocators在堆上stl::allocators

This is quite different from the memory allocated for the container object itself which can be on the stack, heap etc., based on how the container object itself was created. 这与为容器对象本身分配的内存完全不同,容器对象本身可以在堆栈,堆等上,基于容器对象本身的创建方式。 This separation of the container from the allocator is what's preventing some modification to the data from modifying the container object itself. containerallocator这种分离是阻止对数据进行某些修改以修改容器对象本身的原因。

To make debugging your problem simpler, like Jonathan Reinhart suggests, make it a single threaded system, that reads the stream AND parses it. 为了简化调试问题,就像Jonathan Reinhart建议的那样,使它成为一个单线程系统,读取流并解析它。

On a side note, have you considered using Boost Lookfree Queues or something similar. 在旁注中,您是否考虑使用Boost Lookfree Queues或类似的东西。 They are designed exactly for this type of scenarios. 它们专为此类场景而设计。 If you were receiving packets/reading them frequently, locking the queue for reading/writing for each packet can become a significant performance overhead. 如果您经常接收数据包/读取它们,则锁定队列以便为每个数据包进行读/写操作可能会成为显着的性能开销。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM