简体   繁体   English

std :: thread :: join在析构函数中挂起

[英]std::thread::join hangs in destructor

I have the following code that runs functions on a dedicated thread. 我有以下代码在专用线程上运行函数。 It works great except for the destructor. 它除了析构函数外都很好用。 The call to thread_.join() does not return. thread_.join()的调用不会返回。 I am using VS2013 Express. 我正在使用VS2013 Express。

What would I change so that the thread joins correctly? 我会改变什么以便线程正确连接?

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>

namespace
{
    class main_thread
    {
    public:
        static auto instance() -> main_thread&
        {
            static main_thread instance_;
            return instance_;
        }
        auto enque(std::function<void()> func) -> void
        {
            {
                std::lock_guard<std::mutex> lock{ mutex_ };
                queue_.push_back(func);
            }
            condition_.notify_one();
        }
    private:
        main_thread()
        {
            continue_.test_and_set();
            thread_ = std::thread{ std::bind(std::mem_fn(&main_thread::run), this) };
        }
        ~main_thread()
        {
            continue_.clear();
            condition_.notify_all();
            if (thread_.joinable())
            {
                thread_.join();
            }
        }
        main_thread(const main_thread &other) = delete;
        main_thread(main_thread &&other) = delete;
        main_thread& operator=(const main_thread &other) = delete;
        main_thread& operator=(main_thread &&other) = delete;

        auto run() -> void
        {
            while (continue_.test_and_set())
            {
                auto lock = std::unique_lock<std::mutex>{ mutex_ };
                //condition_.wait_for(lock, std::chrono::milliseconds(1));
                condition_.wait(lock);
                for (auto &func : queue_)
                {
                    func();
                }
                queue_.clear();
            }
        }

        std::condition_variable condition_;
        std::mutex mutex_;
        std::vector<std::function<void()>> queue_;
        std::thread thread_;
        std::atomic_flag continue_;
    };
}

auto on_main_thread(std::function<void()> func) -> void
{
    main_thread::instance().enque(std::move(func));
}

auto on_main_thread_sync(std::function<void()> func) -> void
{
    bool done{ false };
    on_main_thread([&]{
        func();
        done = true;
    });
    while (!done);
}

The only function exercising this code is 执行此代码的唯一功能是

int main()
{
    on_main_thread([]{});
}

This avoids the issue of the race in on_main_thread_sync but still has the lockup in ~main_thread . 这避免了on_main_thread_sync中的竞争问题,但仍然在~main_thread具有锁定。 Visual Studio indicates that there are 2 threads, but neither is in main_thread::run , so I do not understand what is going on. Visual Studio表明有2个线程,但是在main_thread::run都没有,所以我不明白发生了什么。 That function exited correctly, but it for some reason the thread is not ending. 该函数正确退出,但由于某种原因线程没有结束。

You should not call external code from within a critical section of code, this can easily lead to deadlocks. 您不应该在代码的关键部分内调用外部代码,这很容易导致死锁。

If you pause execution in the debugger, you may see that you have one or more threads waiting to acquire _mutex. 如果在调试器中暂停执行,您可能会看到有一个或多个线程等待获取_mutex。

You won't be able to acquire the unique_lock on _mutex again if any of the code called from func() tries to enqueue(). 如果从func()调用的任何代码尝试enqueue(),您将无法再次获取unique_lock上的unique_lock。

Try releasing the lock once the condition_variable wait is over. 尝试在condition_variable等待结束后释放锁定。 As a test, you can put in an extra scope to see if this helps: 作为测试,您可以添加额外的范围以查看是否有帮助:

while (continue_.test_and_set())
{
    std::vector<std::function<void()>> queue;
    {
        auto lock = std::unique_lock<std::mutex>{ mutex_ };
        //condition_.wait_for(lock, std::chrono::milliseconds(1));
        condition_.wait(lock);
        queue.swap(queue_);
    }
    for (auto &func : queue)
    {
        func();
    }
}

You have a potential live-lock in your code at shutdown. 在关机时,您的代码中存在潜在的锁定。 The following interleaving is possible: 以下交错是可能的:

main() thread      thread in run()
                   check continue_, see it is true
set continue_ = false
notify the condition variable
join
                   wait on condition variable

To avoid this, you need the condition check and cv wait to happen atomically. 为了避免这种情况,你需要条件检查和cv等待以原子方式发生。 This is most easily accomplished by protecting continue_ with mutex_ ( Live at Coliru ): 这可以通过使用mutex_Live at Coliru )保护continue_来轻松完成:

class main_thread
{
public:
    static auto instance() -> main_thread&
    {
        static main_thread instance_;
        return instance_;
    }
    auto enque(std::function<void()> func) -> void
    {
        {
            std::lock_guard<std::mutex> lock{ mutex_ };
            queue_.push_back(func);
        }
        condition_.notify_one();
    }
private:
    main_thread() : continue_{true}
    {
        thread_ = std::thread{ &main_thread::run, this };
    }
    ~main_thread()
    {
        {
            std::lock_guard<std::mutex> lock{ mutex_ };
            continue_ = false;
        }
        condition_.notify_all();
        if (thread_.joinable())
        {
            thread_.join();
        }
    }

    auto run() -> void
    {
        std::unique_lock<std::mutex> lock{ mutex_ };
        while(continue_)
        {
            if(queue_.empty())
            {
                condition_.wait(lock);
                continue;
            }

            std::vector<std::function<void()>> queue;
            queue.swap(queue_);
            lock.unlock();
            for (auto &func : queue)
            {
                func();
            }
            lock.lock();
        }
    }

    std::condition_variable condition_;
    std::mutex mutex_;
    std::vector<std::function<void()>> queue_;
    bool continue_;
    std::thread thread_;
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM