为什么我会对这个程序“中止”?

时间:2017-02-07 16:24:18

标签: c++ multithreading

让我们了解下一个代码示例:

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <deque>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;
std::deque<int> queue;



void Inserter()
{
    int count = 10;
    while (count >= 0)
    {
        std::unique_lock<std::mutex> lock(mtx);
        queue.push_back(count);
        --count;
        lock.unlock();
    }
}

void Consumer()
{
    int count = 10, got;

    while (count >= 0)
    {
        std::unique_lock<std::mutex> lock(mtx);
        if (!queue.empty())
        {
            got = queue.back();
            queue.pop_back();
            std::cout << "We got: " << got << std::endl;
            --count;
            lock.unlock();
        }
        else
        {
            lock.unlock();
        }
    }
}

int main()
{
    std::thread t1(Inserter);
    std::thread t2(Consumer);

    std::cin.get();
    return 0;
}

当我运行这个程序时,我得到“中止”,但我不应该。 我在这里看到中止的唯一原因是当我离开锁定锁而没有打开它时 - 但没有任何理由让锁保持锁定,因为在我经过的每个循环之后我打开锁。

你认为原因是什么?

2 个答案:

答案 0 :(得分:1)

您不能在主程序中调用std::thread::join。当t2超出范围时,你会调用它的析构函数,并且由于你还没有加入该线程,它会导致中止。

顺便说一句,您不需要致电lock.unlock()std::unique_lock的重点是它会在对象被破坏时自动执行。 (将got声明为从队列中获取值的位置会更加清晰。)

所以你的代码应该是这样的:

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <deque>

std::mutex mtx;
std::deque<int> queue;

void Inserter()
{
    for (int count = 10; count >= 0; --count)
    {
        std::unique_lock<std::mutex> lock(mtx);
        queue.push_back(count);
    }
}

void Consumer()
{
    for (int count = 10; count >= 0; )
    {
        std::unique_lock<std::mutex> lock(mtx);
        if (!queue.empty())
        {
            const int got = queue.back();
            queue.pop_back();
            std::cout << "We got: " << got << std::endl;
            --count;
        }
    }
}

int main()
{
    std::thread t1(Inserter);
    std::thread t2(Consumer);

    t1.join();
    t2.join();

    std::cin.get();
    return 0;
}

调用join的要求是因为一个常见的习惯用法是使Inserter成为派生自std::thread的类,并使Inserter构造函数传递合适的线程函数到std::thread构造函数。如果类包含线程方法访问的成员变量,那么如果线程方法在对象被破坏后继续运行则会很糟糕。该标准要求您通过调用join(在这种情况下线程方法已完成)或通过调用detach来避免这种情况(在这种情况下,您有责任确保不要使用Maven。 t访问超出范围的变量。)

答案 1 :(得分:0)

要解决此问题,您需要在两个线程上调用std::thread::join

int main(){

       std::thread t1(Inserter);      
       std::thread t2(Consumer);
       t1.join();
       t2.join();

    return 0;
}

演示:here