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

时间:2014-12-17 21:54:04

标签: c++ multithreading boost boost-thread

我有一个队列类,其数据存储在向量中:

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

有一个线程添加到基于此循环的队列:

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

其中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());

这个图片在这个帖子中得到了很好的收到(我已经用10,000左右进行了测试,看起来很好)。

runRxThread变量是通过定义线程函数的类中的一些setter函数设置的。

当我在主线程中运行一个进程时,例如:

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();

由clear()引起的段错误。我已经检查过它肯定是这条线而不是对象的内部管道,它肯定是。

这似乎是线程安全问题,但我不知道如何修复它,更重要的是,我不知道为什么。我的理解是两个线程可以写入同一个内存,但不能同时写入。当然,通过设置我的runRxThread变量,我确保不会发生这种情况。

我非常想要一个不涉及互斥锁或信号量的解决方案 - 我真的不认为它们对于像这样的问题是必要的。

谢谢!

编辑:runRXThread是volatile,线程循环现在是:

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

EDIT2:&#34;在共享对象上使用互斥锁&#34;

好的,这显然是一个线程安全问题,我需要让我的共享变量线程安全。但...

1)rxImage();除非有数据被发送,否则不会终止

2)段错误发生在rxImage();

3)如果我用互斥锁锁定队列,那么程序肯定会在rxImage中挂起,直到有数据,因为互斥锁不会被释放

4)没有数据发送,所以程序将永远挂起。

我的理解是不正确的?

EDIT3:

我已将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;   
}

之前的问题显然是在清除队列时写入了localQueue。现在,当有数据要写入队列时,只能在此函数中写入队列。我可以保证当我调用clear()时,没有要写入的数据,((int)img.size()&gt; 0)返回false并且线程不访问队列。为什么还有段错误?当然这证明这个线程不会导致段错误吗?

这是一个终端输出:

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

可以看出线程已完成向量,然后图像服务器停止,然后向量被清除。正是按顺序,没有不可预测的行为。但仍然存在一个段落错误。

6 个答案:

答案 0 :(得分:5)

您的数据竞赛在这里:

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

你没有检查runTxThread()循环的持续时间(更不用说除非runRxThread被标记为volatile,否则它甚至可能不会从主内存中读取,但是“假定”在登记册。

注意即使是volatile,我也只是指出编译器假定一个单线程的抽象机器,除非采用明确的atomic内存排序模式)

你需要互相排斥。

答案 1 :(得分:4)

从两个线程访问可变共享数据时,您需要防止数据争用。问题看起来有多简单并不重要,如果代码有数据竞争,则无法保证代码的正确性。典型的解决方案是使用互斥锁等来确保只有一个线程同时访问共享状态。如果您使用的队列是线程安全的(std :: vector显然不是),则不必手动执行此操作。

以下是线程安全队列的示例,但它似乎没有clear()操作:http://www.boost.org/doc/libs/1_53_0/doc/html/boost/lockfree/queue.html。实际上,它是无锁的,因此它不使用互斥锁,但这并不意味着它比具有互斥锁的线程安全队列更简单。它实际上是另一种方式 - 很难编写正确的无锁代码。

答案 2 :(得分:3)

问题在于,即使您将runRxThread设置为false,该线程仍可能在this->rxImage()内执行操作并且可能正在访问该向量。在允许主线程清除向量之前,您需要等待它完成并再次检查循环条件。清除&#39;清除它并不好。线程仍在访问它时的向量。

因此,在允许主线程清除向量之前,您需要等待this->rxImage()完成。

一个解决方案是让StopRx()等待你的&#34;线程添加到队列&#34;在设置runRxThread false之后调用thread.join()来完成(假设你正在使用std :: thread)。

我建议您也将runRxThread更改为键入std :: atomic,以确保两个线程始终具有一致的视图值。

答案 3 :(得分:3)

  

我的理解是两个线程可以写入同一个内存,但不能同时写入。

除非您向代码添加显式同步(例如,使用互斥锁,信号量或原子操作),否则您无法有意义地说出两个事件是否发生在同一时间&#34;或不。如果没有同步,你就不能说一个甚至在另一个之前发生。

  

我非常想要一个不涉及互斥锁或信号量的解决方案 - 我真的不认为它们对于像这样的问题是必要的。

你错了。你需要像互斥体这样的东西或者像使用原子操作的无锁队列那样很多更复杂的东西。

由于您不是该领域的专家,只需使用互斥锁来保护您从多个线程访问的所有共享数据(除非所有访问只是读取且没有写入)。 / p>

答案 4 :(得分:2)

我是OP,我已经解决了这个问题。

该问题显然不是其他用户建议的线程争用问题。这在原始问题的编辑3中得到证实。终端输出模拟互斥锁将被锁定和释放的位置,并证明在这种情况下它们是必需的 - 因为线程通过网络同步。我承认这是一个非常少数的案例。

我将问题追溯到正在排队的图像类的析构函数,删除了一个变量,这会导致段错误。

答案 5 :(得分:0)

如果使用 Mutex 进行并发,则锁定是不错的选择。您可以在类的push,pop方法中使用锁。

当您按下,弹出或访问数据 Mutex 锁定时,将确保线程安全。

让我演示一下 -

假设您在执行操作时正在访问队列,我们​​假设队列大小为5.如果使用推送时使用的相同 Mutex 锁定锁定该操作的块,则弹出队列中的其他操作。然后,在执行操作块之前,不会执行队列中的其他操作。

例如,请参阅此简单的伪代码示例

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
}

请注意,如果没有以正确的方式使用 Mutex 锁,可能会导致死锁。因此,请谨慎使用 Mutex