在C ++中阻塞队列和屏障的死锁

时间:2018-06-01 23:18:56

标签: c++ multithreading threadpool deadlock blockingqueue

我有这个非常简单的小型C ++程序,可以创建线程池,然后将消息放在线程之间共享的阻塞队列中对每个线程说什么。

消息可以是: -1 (流结束 - >终止), -2 (屏障 - >等待所有线程到达它,然后继续),其他值进行随机计算。循环按以下顺序完成:一些计算,屏障,一些计算,屏障,......,屏障,流结束,线程连接,退出。

即使池中有2个线程,我也无法理解为什么我会获得死锁。队列不能变空,但是我推送和弹出消息的顺序总是会导致空队列!

阻塞队列实现是这里提出的(C++ Equivalent to Java's BlockingQueue),只添加了两个方法。我还复制了下面的队列代码。

任何帮助?

Main.cpp的

#include <iostream>
#include <vector>
#include <thread>
#include "Queue.hpp"

using namespace std;

// function executed by each thread
void f(int i, Queue<int> &q){
    while(1){
        // take a message from blocking queue
        int j= q.pop();
        // if it is end of stream then exit
        if (j==-1) break;
        // if it is barrier, wait for other threads to reach it
        if (j==-2){
            // active wait! BAD, but anyway...
            while(q.size() > 0){
                ;
            }
        }
        else{
            // random stuff
            int x = 0;
            for(int i=0;i<j;i++)
                x += 4;
        }
    }
}

int main(){
    Queue<int> queue; //blocking queue
    vector<thread> tids; // thread pool
    int nt = 2; // number of threads
    int dim = 8; // number to control number of operations

    // create thread pool, passing thread id and queue
    for(int i=0;i<nt;i++)
        tids.push_back(thread(f,i, std::ref(queue)));

    for(int dist=1; dist<=dim; dist++){ // without this outer loop the program works fine

        // push random number
        for(int j=0;j<dist;j++){    
            queue.push(4);  
        }

        // push barrier code
        for(int i=0;i<nt;i++){
            queue.push(-2);
        }

        // active wait! BAD, but anyway...
        while (queue.size()>0){
                 ;
        }
    }
    // push end of stream
    for(int i=0;i<nt;i++)
        queue.push(-1);
    // join thread pool
    for(int i=0;i<nt;i++){
        tids[i].join();
    }           
return 0;
}

Queue.hpp

#include <deque>
#include <mutex>
#include <condition_variable>

template <typename T>
class Queue
{
private:
  std::mutex              d_mutex;
  std::condition_variable d_condition;
  std::deque<T>           d_queue;
public:

  void push(T const& value) {
    {
      std::unique_lock<std::mutex> lock(this->d_mutex);
      d_queue.push_front(value);
    }
    this->d_condition.notify_one();
  }

  T pop() {
    std::unique_lock<std::mutex> lock(this->d_mutex);
    this->d_condition.wait(lock, [=]{ return !this->d_queue.empty(); });
    T rc(std::move(this->d_queue.back()));
    this->d_queue.pop_back();
    return rc;
  }

  bool empty(){
      std::unique_lock<std::mutex> lock(this->d_mutex); 
      return this->d_queue.empty(); 
  }

  int size(){
    std::unique_lock<std::mutex> lock(this->d_mutex); 
    return this->d_queue.size();
  }
};

2 个答案:

答案 0 :(得分:0)

我认为问题在于你的积极等待,你描述为&#34; BAD,但无论如何......&#34;并使用队列的大小作为障碍,而不是使用真正的synchronization barrier

对于dim = 1,你推送一个有4,-2,-2的队列。一个线程将抓住4和-2而另一个线程抓住剩余的-2。此时队列为空,您有三个线程(两个工作线程和主线程)正在进行主动等待,以查看队列是否已清空。大小互联网只允许一次读取大小。如果首先调度主线程并确定该队列为空,则它将按下-1,-1以指示流的结束。现在,队列不再是空的,但是两个工作线程中的一个或两个正在等待它清空。由于他们在获取另一个项目之前等待它为空,因此队列在此状态下处于死锁状态。

对于案件是dim&gt; 1在将两个工作确认清空队列并退出活动等待之前,将下一组值推入主线程的队列中可能存在类似的问题。

答案 1 :(得分:-1)

我运行了你的代码,我理解了这个问题。问题出在&#34; -2&#34;选项。当两个线程到达此时,您的主线程已经将另一个值推送到队列中。所以,如果你的队列在线程获得的时间和#34; -2&#34;之间增加了它的大小。价值,在他们到达&#34; -2&#34;之前选项,您的代码将卡住: 线程1:得-2。 线程2:得-2。 线程主:推-1。 线程主:推-1。 线程1:等待直到整个队列都为空。 线程2:等待直到整个队列都是空的。

队列: -1 -1

^如果dim等于1.在你的代码中,dim等于8,你不想看到它的样子。 为了解决这个问题,我所做的就是禁用以下循环:

username

当这个pard禁用时,代码运行完美。 这是我检查它的方式:

for(int i=0;i<nt;i++){
    queue.push(-2);
}

结果:

std::mutex guarder;

// function executed by each thread
void f(int i, Queue<int> &q){
    while(1){
        // take a message from blocking queue
        guarder.lock();
        int j= q.pop();
        guarder.unlock();
        // if it is end of stream then exit
        if (j==-1) break;
        // if it is barrier, wait for other threads to reach it
        if (j==-2){
            // active wait! BAD, but anyway...
            while(q.size() > 0){
                ;
            }
        }
        else{
            // random stuff
            int x = 0;
            for(int i=0;i<j;i++)
                x += 4;
            guarder.lock();
            cout << x << std::endl;
            guarder.unlock();
        }
    }
}

int main(){
    Queue<int> queue; //blocking queue
    vector<thread> tids; // thread pool
    int nt = 2; // number of threads
    int dim = 8; // number to control number of operations

    // create thread pool, passing thread id and queue
    for(int i=0;i<nt;i++)
        tids.push_back(thread(f,i, std::ref(queue)));

    for(int dist=1; dist<=dim; dist++){ // without this outer loop the program works fine

        // push random number
        for(int j=0;j<dist;j++){
            queue.push(dist);
        }

        /*// push barrier code
        for(int i=0;i<nt;i++){
            queue.push(-2);
        }*/

        // active wait! BAD, but anyway...
        while (queue.size()>0){
            ;
        }
    }
    // push end of stream
    for(int i=0;i<nt;i++)
        queue.push(-1);
    // join thread pool
    for(int i=0;i<nt;i++){
        tids[i].join();
    }
    return 0;
}
顺便说一下,卡住了没有发生,因为你的'等待'#34;部分。它不好,但通常会导致其他问题(比如减慢系统速度)。