死锁使用std :: thread和std :: condition_variable

时间:2015-06-26 08:25:10

标签: c++ multithreading c++11 condition-variable stdthread

我正在调查当我试图阻止它时我的工作线程死锁的问题。

这是有问题的最小版本:

#include <atomic>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>

class Worker
{
private:
    std::atomic<bool>       m_running;
    std::condition_variable m_cond;
    std::mutex              m_mutex;
    std::function<void()>   m_workItem;
    std::thread             m_thread;

public:
    Worker() :
        m_running(true),
        m_thread(std::bind(&Worker::DoWork, this))
    {

    }

    ~Worker()
    {
        Stop();
        m_thread.join();
    }

    bool QueueWork(std::function<void()> item)
    {
        std::unique_lock<std::mutex> padlock(m_mutex);

        if (m_workItem)
        {
            return false;
        }

        m_workItem = item;
        padlock.unlock();
        m_cond.notify_all();
        return true;
    }

    void Stop()
    {
        bool expected = true;
        if (m_running.compare_exchange_strong(expected, false))
        {
            m_cond.notify_all();
        }
    }

private:
    void DoWork() noexcept
    {
        while (m_running)
        {
            std::unique_lock<std::mutex> padlock(m_mutex);
            m_cond.wait(padlock,
                [this]
                ()
                {
                    return !m_running || m_workItem;
                });
            if (m_workItem)
            {
                decltype(m_workItem) workItem;
                std::swap(m_workItem, workItem);
                padlock.unlock();
                workItem();
            }
        }
    }

};

int main()
{
    std::cout << "Start." << std::endl;

    {
        std::vector<std::unique_ptr<Worker>> workers;
        for (int i = 0; i < 10; ++i)
        {
            workers.push_back(std::unique_ptr<Worker>(new Worker()));
        }

        workers[0]->QueueWork(
            []()
            {
                std::cout << "Work item" << std::endl;
            });

        std::this_thread::sleep_for(std::chrono::milliseconds(10));

        for (auto & worker : workers)
        {
            worker->Stop();
        }
        for (auto & worker : workers)
        {
            worker.reset();
        }
    }

    std::cout << "Stop." << std::endl;
    return 0;
}

我们的想法是,当从主机线程调用Worker::Stop()时,Worker::m_running设置为falsenotify_all()会调用Worker::m_cond。工作线程从其m_cond.wait()中醒来,检查m_running并中断,退出线程函数。

然而,有时,这种僵局。工作线程醒来,发现m_runningtrue(这怎么可能?)并返回wait()。没有对m_cond.notify_all()的额外调用,因此线程最终处于死锁状态。

我在此代码中生成了10个Worker个对象。我不认为它与线程数有任何关系,但是为了能够触发竞争条件(如果它就是这样),我需要更多的线程。

代码有什么问题?

运行gcc:

g++ --version
g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

使用编译:

g++ -std=c++11 -pedantic test.cpp -lpthread

编辑: 更改了Worker个私人成员的顺序。 m_thread现在是最后一次。仍然是同样的问题。

0 个答案:

没有答案