简体   繁体   English

C ++清除队列和线程安全

[英]C++ clearing queues and thread safety

I have a queue class, the data of which is stored in a vector: 我有一个队列类,其数据存储在向量中:

std::vector<boost::shared_ptr<rxImage> > queue;

There is a thread which adds to a queue which is based on this loop: 有一个线程添加到基于此循环的队列:

while(runRxThread){
  this->rxImage();
}

Where rxImage() is defined by: 其中rxImage()的定义如下:

zmq::message_t img;
imageSocket->recv(&img);

//addToQueue is a push back:
//queue.push_back( boost::shared_ptr<rxImage> (new rxImage(data, imgSize)) );
localQueue->addToQueue((unsigned char*) img.data());

The images are received fine within this thread (I've tested with 10,000 or so and it seems to be fine). 这个图像在这个帖子中得到了很好的接收(我已经测试了10,000左右,看起来很好)。

The runRxThread variable is set through some setter functions within the class that the thread function is defined in. runRxThread变量是通过定义线程函数的类中的一些setter函数设置的。

When I run a process in the main thread such as: 当我在主线程中运行一个进程时,例如:

startRx(); //start the thread

/*process to stimulate the sending of network data from another program*/

stopRX(); //stop the thread from accessing the queue

queue.clear();

There is a segfault caused by the clear(). 由clear()引起的段错误。 I have checked it is definitely this line and not the internal plumbing of the objects, and it definitely is. 我已经检查过它肯定是这条线而不是对象的内部管道,它绝对是。

It appears to be a thread safety issue but I don't know how to fix it and, more importantly, I don't know why. 它似乎是一个线程安全问题,但我不知道如何解决它,更重要的是,我不知道为什么。 My understanding is that two threads can write to the same memory, but not at the same time. 我的理解是两个线程可以写入同一个内存,但不能同时写入。 Surely by setting my runRxThread variables, I ensure that this does not happen. 当然,通过设置我的runRxThread变量,我确保不会发生这种情况。

I would very much like a solution that does not involve mutexes or semaphores - I really don't think they should be necessary for a problem like this. 我非常想要一个不涉及互斥锁或信号量的解决方案 - 我真的不认为它们对于像这样的问题是必要的。

Thanks! 谢谢!

EDIT: runRXThread is volatile and the thread loop is now: 编辑:runRXThread是volatile并且线程循环现在是:

while(1){
    if(runRxThread == 1){
      this->rxImage();
    }
}

EDIT2: "use a mutex on shared objects" EDIT2:“在共享对象上使用互斥锁”

OK, this is clearly a thread safety issue, I need to make my shared variables threadsafe. 好吧,这显然是一个线程安全问题,我需要让我的共享变量线程安全。 But... 但...

1) rxImage(); 1)rxImage(); does not terminate unless there is data being sent 除非有数据发送,否则不会终止

2) The segfault happens within rxImage(); 2)段错误发生在rxImage()中;

3) If I lock the queue with a mutex, surely the program will hang in rxImage until there is data, because the mutex will not be released 3)如果我用互斥锁锁定队列,那么程序肯定会在rxImage中挂起,直到有数据,因为互斥锁不会被释放

4) There will be no data sent, so the program will hang forever. 4)没有数据发送,所以程序将永远挂起。

Is my understanding here incorrect? 我的理解不正确吗?

EDIT3: EDIT3:

I have changed rxImage() to be non blocking: 我已将rxImage()更改为非阻塞:

zmq::message_t img;
imageSocket->recv(&img,ZMQ_NOBLOCK);
if((int)img.size() > 0){
    cout<<"in the thread conditional"<<endl;     
    localQueue->addToQueue((unsigned char*) img.data());
    cout<<"leaving thread conditional"<<endl;   
}

The problem earlier was apparently that localQueue was being written to when I was clearing the queue. 之前的问题显然是在我清理队列时写入了localQueue。 Now, the queue can only be written in this function when there is data to write to it. 现在,当有数据要写入队列时,只能在此函数中写入队列。 I can guarantee that when I call the clear(), there is no data to write, ((int)img.size() > 0) returns false and the queue is not accessed by the thread. 我可以保证当我调用clear()时,没有要写入的数据,((int)img.size()> 0)返回false并且线程不访问队列。 Why is there still a segfault? 为什么还有段错误? Surely this proves that this thread does not cause the segfault? 当然这证明这个线程不会导致段错误?

Here is a terminal output: 这是一个终端输出:

in the thread
pushing back1 of size: 16000000
Added image to queue. queue size: 650
leaving thread conditional

image server stopped
stopping image server
clearing vector
Segmentation fault

It can be seen that the thread is finished with the vector, then the image server is stopped, then the vector is cleared. 可以看出,线程用向量完成,然后图像服务器停止,然后向量被清除。 Precisely in that order with no unpredicted behaviour. 正是按顺序,没有不可预测的行为。 But there is still a segfault. 但仍然存在一个段落错误。

Your data race is here: 您的数据竞争在这里:

while(runRxThread){
  this->rxImage();
}

You don't check runTxThread() for the duration of the loop (not to mention that unless runRxThread is marked volatile, it might not even be read from main memory, but "assumed" unchanged in a register. 在循环期间不检查runTxThread() (更不用说除非runRxThread标记为volatile,否则它甚至可能无法从主内存中读取,但在寄存器中“假定”未更改。

( NOTE even with volatile the race is there I was merely pointing out the compiler assumes a single-threaded abstract machine, unless the explicit atomic memory ordering modes are employed) 注意即使使用volatile,我也只是指出编译器假定一个单线程抽象机器,除非采用显式atomic内存排序模式)

You need mutual exclusion. 你需要互相排斥。

When accessing mutable shared data from two threads, you need to protect against data races. 从两个线程访问可变共享数据时,您需要防止数据争用。 It does not matter how simple your problem might look like, you cannot guarantee correctness of your code if it has a data race. 问题看起来有多简单并不重要,如果代码有数据竞争,则无法保证代码的正确性。 A typical solution is using a mutex or the like to ensure that only one thread accesses the shared state at the same time. 典型的解决方案是使用互斥锁等来确保只有一个线程同时访问共享状态。 You wouldn't have to do this manually if the queue you are using was thread-safe (std::vector is obviously not). 如果您使用的队列是线程安全的(std :: vector显然不是),则不必手动执行此操作。

Here is an example of a thread-safe queue, but it does not seem to have the clear() operation: http://www.boost.org/doc/libs/1_53_0/doc/html/boost/lockfree/queue.html . 下面是一个线程安全队列的示例,但它似乎没有clear()操作: http//www.boost.org/doc/libs/1_53_0/doc/html/boost/lockfree/queue。 HTML In fact, it is lock-free, so it does not use a mutex, but this does not mean it is simpler than a thread-safe queue with a mutex. 实际上,它是无锁的,因此它不使用互斥锁,但这并不意味着它比具有互斥锁的线程安全队列更简单。 It is actually the other way around - it is hard to write correct lock-free code. 它实际上是另一种方式 - 很难编写正确的无锁代码。

The problem is that even if you set runRxThread to false, the thread may still be doing stuff within this->rxImage() and could be accessing the vector. 问题是即使你将runRxThread设置为false,该线程仍然可以在this->rxImage()并且可以访问该向量。 You need to wait for it to finish doing that and check the loop condition again before allowing the main thread to clear the vector. 在允许主线程清除向量之前,您需要等待它完成并再次检查循环条件。 It wouldn't be good to 'clear' the vector while a thread is still accessing it. 当一个线程仍在访问它时,“清除”向量并不好。

So, you need to wait for this->rxImage() to complete before allowing the main thread to clear the vector. 因此,在允许主线程清除向量之前,需要等待this->rxImage()完成。

One solution would be to get StopRx() to wait for your "thread which adds to the queue" to complete by calling thread.join() after setting runRxThread false (assuming you're using a std::thread). 一种解决方案是在设置runRxThread false之后通过调用thread.join()来让StopRx()等待你的“添加到队列的线程”完成(假设你正在使用std :: thread)。

I'd suggest you also change runRxThread to type std::atomic to ensure both threads always have a consistent view of what it's value is. 我建议您也更改runRxThread以键入std :: atomic,以确保两个线程始终具有一致的视图值。

My understanding is that two threads can write to the same memory, but not at the same time. 我的理解是两个线程可以写入同一个内存,但不能同时写入。

Unless you add explicit synchronisation to your code (eg using mutexes, semaphores, or atomic operations) you cannot meaningfully say whether two events happen "at the same time" or not. 除非您向代码添加显式同步(例如,使用互斥锁,信号量或原子操作),否则无法有意义地说明两个事件是否“同时”发生。 Without synchronisation you cannot say one even happens before the other. 没有同步,你不能说一个甚至在另一个之前发生。

I would very much like a solution that does not involve mutexes or semaphores - I really don't think they should be necessary for a problem like this. 我非常想要一个不涉及互斥锁或信号量的解决方案 - 我真的不认为它们对于像这样的问题是必要的。

You're wrong. 你错了。 You either need something like a mutex or something much more complicated like a lock-free queue using atomic operations. 你要么需要像一个互斥体或一些复杂,喜欢使用原子操作无锁队列。

Since you are not an expert in this area, just use a mutex to protect all shared data that you access from multiple threads (unless all accesses are only reads and there are no writes). 由于您不是该领域的专家,只需使用互斥锁来保护您从多个线程访问的所有共享数据(除非所有访问只是读取且没有写入)。

I am the OP, I have fixed the problem. 我是OP,我已经解决了这个问题。

The issue is clearly not a thread contention issue, as suggested by other users. 根据其他用户的建议,该问题显然不是线程争用问题。 This is proven in edit 3 of the original question. 这在原始问题的编辑3中得到证实。 The terminal output simulates where a mutex would have been locked and released, and proves that they are necessary in this case - as the threads are synchronised over the network. 终端输出模拟互斥锁将被锁定和释放的位置,并证明在这种情况下它们是必需的 - 因为线程通过网络同步。 I accept that this is a very minority case. 我承认这是一个非常少数的案件。

I traced the problem back to the destructor of the image class which is being queued, a variable is deleted and this causes the segfault. 我将问题追溯到正在排队的图像类的析构函数,删除了一个变量,这会导致段错误。

In case of concurrency using Mutex locks are good option. 如果使用Mutex并发锁是很好的选择。 You can use the locks in your class's push, pop methods. 您可以在类的push,pop方法中使用锁。

When you are pushing, popping or accessing data Mutex locks will make sure the thread safety. 当您推送,弹出或访问数据时, Mutex锁将确保线程安全。

Let me demonstrate this - 让我证明这一点 -

Suppose you are accessing the queue while doing an operation, lets assume the queue size is 5. If you lock the block of that operation using the same Mutex lock that are being used while push, pop and other operations in queue. 假设您在执行操作时正在访问队列,假设队列大小为5.如果使用在推送,弹出和队列中的其他操作时使用的相同互斥锁来锁定该操作的块。 Then other operations in the queue will not be executed until the operation block has been executed. 然后,在执行操作块之前,不会执行队列中的其他操作。

For example see this simple pseudo code example 例如,请参阅此简单的伪代码示例

CQUEUE::Push(element)
{
    Mutex lock; // this is a pseudo code please ignore syntax error. You can find the exact syntax in any where through the web :P
    AddToQUEUE(element);

   //other operations under the same mutex will not be executed if the mutex lock variable is same
}

Please note that using Mutex locks may cause deadlocks if it is not being used in a proper way. 请注意,如果没有以正确的方式使用Mutex锁,可能会导致死锁。 So, please use Mutex with great care. 因此,请谨慎使用Mutex

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

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