【问题标题】:Segfault occuring in attempt to synchronize queue尝试同步队列时发生段错误
【发布时间】:2017-03-15 20:57:24
【问题描述】:

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

我有一个包含队列的类,生产者将整数推送到队列中,消费者检索并打印它。我模拟如下

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 它抛出段错误。我不确定为什么我对代码的理解如下:让我们假设 order 2 1 1

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

这个逻辑正确吗?如果是这样,为什么这会一直抛出段错误,如果不是,我感谢所有解释。

感谢您的帮助!

//编辑 通过回答建议,我将 [] 替换为 vector.push_back ,但消费者现在对数据不做任何事情,不接受或打印它。

【问题讨论】:

  • 错字,谢谢!更新了
  • 哦,再次感谢:)

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


【解决方案1】:

当你这样做时,你并没有扩展 thrs 向量

thrs[i] = thread(&CTest::consumer, this );

你应该这样做

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

这就是崩溃的地方。

【讨论】:

  • 注意到它并添加了 push_back ,但是消费者现在对数据什么都不做,它不会拿走它并打印它。
  • 如果没有 5 个输入等待,ConsumerInit 将无限期阻塞(因此程序不能再做任何输入)。你输入的是“2 1 1”吗?
  • users inpu 是 2 1 1 ,所以它创建线程,然后将号码推入队列两次
  • 它创建5个线程,然后等待它们终止,然后输入另一个数字。
  • 我明白你的意思,但是当我删除 join() 并输入 1 1 1 1 1 1 2 时,会打印 5 个数字,因为 thred 需要它们,但我怎样才能让它们打印所有数字,在这种情况下是全部 6 个,加入它们后,当队列为空时终止线程?
【解决方案2】:

您的问题与多线程无关。您正在访问 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 异常而不是分段错误。

【讨论】:

  • 注意到它并添加了 push_back ,但是消费者现在对数据什么都不做,它不会拿走它并打印它。
  • 那完全是另一个问题了。
【解决方案3】:

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

原因如下:

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

【讨论】:

    【解决方案4】:

    问题来了:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-08
      • 2020-05-28
      相关资源
      最近更新 更多