尝试同步队列

时间:2017-03-15 20:57:24

标签: c++ multithreading mutex semaphore data-race

我正在学习多线程,我想模拟生产者 - 消费者问题(使用信号量,如果我可以称之为)。

我有一个持有队列的类,生产者推入队列,消费者检索它并打印它。我模拟如下

class TestClass{
public:
    void producer( int i ){
        unique_lock<mutex> l(m);
        q.push(i);
        if( q.size() )
            cnd.notify_all();
    }

    void consumer(){
        unique_lock<mutex> l(m);
        while( q.empty() ){
            cnd.wait(l);
        }
        int tmp = q.front();
        q.pop();
        cout << "Producer got " << tmp << endl;
    }
    void ConsumerInit( int threads ){
        for( int i = 0; i < threads; i++ ){
            thrs[i] = thread(&TestClass::consumer, this );
        }
        for( auto &a : thrs )
            a.join();
    }


private:
    queue<int> q;
    vector<thread> thrs;
    mutex m;
    condition_variable cnd;
};

我使用了一个小的控制台应用程序来调用数据:

int main(){
    int x;   
    TestClass t;
    int counter = 0;
    while( cin >> x ){
        if( x == 0 )
            break;
        if( x == 1)
            t.producer(counter++);
        if( x == 2 )
            t.ConsumerInit(5);
    }   
}

因此,当用户输入1时,数据被推入队列,如果用户按下2个线程则会产生。

按照调用它的任何顺序,例如,按1 1然后按2或2 1 1 它引发了段错误。我不确定为什么我对我的代码的理解如下:让我们假设顺序为2 1 1

我初始化了5个线程,他们看到队列是空的,所以他们进入睡眠状态。当我将一个数字推送到队列时,它会通知所有正在休眠的线程。 第一个再次唤醒锁定互斥锁并继续从队列中检索数字然后释放互斥锁,当互斥锁被释放时,另一个线程执行相同操作并解锁互斥锁,互斥锁解锁后的第三个线程仍在循环中并看到队列再次为空并再次进入休眠状态,与所有剩余的线程相同。

这个逻辑是否正确?如果是这样,为什么这会继续抛出段错误,如果没有,我感谢所有的解释。

感谢您的帮助!

//编辑 通过回答suggets,我用vector.push_back替换了[],但是消费者现在对数据没有任何作用,不接受或打印它。

4 个答案:

答案 0 :(得分:1)

当你做

时,你没有扩展矢量
thrs[i] = thread(&CTest::consumer, this );

你应该做

thrs.emplace_back(&CTest::consumer, this);

这就是崩溃的原因。

答案 1 :(得分:1)

您的问题与多线程无关。您正在访问std::vector越界:

  for (int i = 0; i < threads; i++) {
        thrs[i] = thread(&CTest::consumer, this);

  //...
  vector<thread> thrs;

thrs向量为空,您正在尝试访问,就好像它有条目一样。

要显示错误,请使用:

        thrs.at(i) = thread(&CTest::consumer, this);

您将收到std::out_of_range例外情况,而不是细分错误。

答案 2 :(得分:1)

如果输入序列不是1 1 1 1 1 ... 2的形式,则程序会死锁。也就是说,1s之前2的数字小于5。

原因如下:

如果队列大小中的总元素小于5且主线程调用consumerInit,则五个创建的使用者线程中的一些将阻止等待队列接收元素。同时,主线程在join操作上阻塞。由于主线程将等待消费者线程完成而其中一些线程正在等待数据消耗,因此将没有进展。因此陷入僵局。

答案 3 :(得分:1)

问题出在这里:

  for( auto &a : thrs )
        a.join();

在您输入2等待消费者完成后,主线程会被阻止。因此,在此之后,您认为您正在输入输入,而没有cin发生。

删除这两行,然后您可以输入1,生产者/消费者将完成他们的工作。