使用boost :: thread发生线程锁。我的病情变量出了什么问题?

时间:2013-01-13 04:56:17

标签: c++ multithreading boost condition-variable

我编写了一个Link类,用于在网络中的两个节点之间传递数据。我用两个deques(一个用于从节点0到节点1的数据,另一个用于从节点1到节点0的数据)实现它。我正在尝试多线程应用程序,但我得到了线程锁。我试图阻止同时读取和写入同一个双端队列。在阅读更多关于我最初如何实现这一点时,我认为我正在使用条件变量错误(也许不应该使用布尔变量?)。我应该有两个互斥,每个双端一个吗?如果可以的话请帮忙。谢谢!

class Link {
public:
// other stuff...
void push_back(int sourceNodeID, Data newData);
void get(int destinationNodeID, std::vector<Data> &linkData);

private:
// other stuff...
std::vector<int> nodeIDs_;
// qVector_ has two deques, one for Data from node 0 to node 1 and 
// one for Data from node 1 to node 0
std::vector<std::deque<Data> > qVector_; 
void initialize(int nodeID0, int nodeID1);

boost::mutex mutex_;
std::vector<boost::shared_ptr<boost::condition_variable> > readingCV_;
std::vector<boost::shared_ptr<boost::condition_variable> > writingCV_;
std::vector<bool> writingData_;
std::vector<bool> readingData_;
};

push_back功能:

void Link::push_back(int sourceNodeID, Data newData)
{
int idx;
if (sourceNodeID == nodeIDs_[0]) idx = 1;
else 
{
    if (sourceNodeID == nodeIDs_[1]) idx = 0;
    else throw runtime_error("Link::push_back: Invalid node ID");
}

boost::unique_lock<boost::mutex> lock(mutex_);
// pause to avoid multithreading collisions
while (readingData_[idx]) readingCV_[idx]->wait(lock);

writingData_[idx] = true;
qVector_[idx].push_back(newData);
writingData_[idx] = false;
writingCV_[idx]->notify_all();
}

获取功能:

void Link::get(int destinationNodeID,
std::vector<Data> &linkData)
{
int idx;
if (destinationNodeID == nodeIDs_[0]) idx = 0;
else 
{
    if (destinationNodeID == nodeIDs_[1]) idx = 1;
    else throw runtime_error("Link::get: Invalid node ID");
}

boost::unique_lock<boost::mutex> lock(mutex_);
// pause to avoid multithreading collisions
while (writingData_[idx]) writingCV_[idx]->wait(lock);
readingData_[idx] = true;

std::copy(qVector_[idx].begin(),qVector_[idx].end(),back_inserter(linkData));
qVector_[idx].erase(qVector_[idx].begin(),qVector_[idx].end());
readingData_[idx] = false;
readingCV_[idx]->notify_all();
return;
}

并且这里初始化(如果它有用)

void Link::initialize(int nodeID0, int nodeID1)
{
readingData_ = std::vector<bool>(2,false);
writingData_ = std::vector<bool>(2,false);
for (int i = 0; i < 2; ++i)
{
    readingCV_.push_back(make_shared<boost::condition_variable>());
    writingCV_.push_back(make_shared<boost::condition_variable>());
}
nodeIDs_.reserve(2);
nodeIDs_.push_back(nodeID0);
nodeIDs_.push_back(nodeID1);
qVector_.reserve(2);
qVector_.push_back(std::deque<Data>());
qVector_.push_back(std::deque<Data>());
}

2 个答案:

答案 0 :(得分:2)

我正在尝试多线程应用程序,但我正在获得线程锁。

什么是“threadlock”?很难看出你的代码想要完成什么。首先考虑你的push_back()代码,其同步部分如下所示:

boost::unique_lock<boost::mutex> lock(mutex_);

while (readingData_[idx]) readingCV_[idx]->wait(lock);

writingData_[idx] = true;
qVector_[idx].push_back(newData);
writingData_[idx] = false;
writingCV_[idx]->notify_all();

您的writingData[idx]布尔值从false开始,并且在线程锁定互斥锁时仅暂时变为true。释放互斥锁时,它再次为假。因此,对于必须等待获取互斥锁的任何其他线程,writingData[idx] 永远不会为真。

但是在你的get()代码中,你有

boost::unique_lock<boost::mutex> lock(mutex_);
// pause to avoid multithreading collisions
while (writingData_[idx]) writingCV_[idx]->wait(lock);

当一个线程锁定互斥锁时,writingData[idx]将返回false,因此while循环(并在CV上等待)从不输入。

完全对称的分析适用于readingData[idx]布尔值,在互斥锁之外总是为假。

所以你的条件变量从不等待。您需要完全重新考虑您的设计。

从每个队列的一个互斥锁开始(deque对于简单地传递数据来说是过度杀伤),并且对于每个队列,将条件变量与非空的队列相关联。因此get()方法将等待,直到队列非空,这将在push_back()方法中发出信号。这样的事情(未经测试的代码):

template <typename Data>
class BasicQueue
{
public:
    void push( Data const& data )
    {
        boost::unique_lock  _lock( mutex_ );
        queue_.push_back( data );
        not_empty_.notify_all();
    }

    void get ( Data& data )
    {
        boost::unique_lock  _lock( mutex_ );
        while ( queue_.size() == 0 )
            not_empty_.wait( _lock ); // this releases the mutex
        // mutex is reacquired here, with queue_.size() > 0
        data = queue_.front();
        queue_.pop_front();         
    }

private:
    std::queue<Data>            queue_;
    boost::mutex                mutex_;
    boost::condition_variable   not_empty_;
};

答案 1 :(得分:1)

是。你需要两个互斥锁。你的僵局几乎肯定是单个互斥锁争用的结果。如果您使用调试器进入正在运行的程序,您将看到线程挂起的位置。另外我不明白为什么你需要bools。 (编辑:有可能提出一种使用单个互斥锁的设计,但每个共享数据结构坚持使用一个互斥锁更简单,更安全)

根据经验,您要保护的每个共享数据结构都有一个互斥锁。该互斥锁保护数据结构免受并发访问,并提供线程安全性。在你的情况下,每个deque一个互斥。 E.g:

class SafeQueue
{
private:
  std::deque<Data> q_;
  boost::mutex m_;
  boost::condition_variable v_;

public:
  void push_back(Data newData)
  {
    boost::lock_guard<boost::mutex> lock(m_);
    q_.push_back(newData);
    // notify etc.
  }
// ...
};

关于通过条件变量的通知,请参见此处:

Using condition variable in a producer-consumer situation

因此每个对象也会有一个condition_variable生产者会通知消费者等待。现在,您可以创建其中两个队列,以便在两个方向上进行通信。请记住,只有两个线程,如果两个线程都被阻塞(等待数据)并且两个队列都是空的,你仍然可以死锁。