我有以下代码在专用线程上运行函数。它除了析构函数外都很好用。对thread_.join()
的调用不会返回。我正在使用VS2013 Express。
我会更改什么以便线程正确连接?
#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);
}
执行此代码的唯一功能是
int main()
{
on_main_thread([]{});
}
这可以避免on_main_thread_sync
中的竞赛问题,但~main_thread
仍然存在锁定问题。 Visual Studio表示有2个线程,但都不在main_thread::run
,所以我不明白发生了什么。该函数正确退出,但由于某种原因线程没有结束。
答案 0 :(得分:1)
您不应该在代码的关键部分内调用外部代码,这很容易导致死锁。
如果在调试器中暂停执行,您可能会看到有一个或多个线程等待获取_mutex。
如果从func()调用的任何代码尝试入队(),您将无法再次获取_mutex上的unique_lock
。
尝试在condition_variable
等待结束后释放锁定。作为测试,您可以添加额外的范围以查看是否有帮助:
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();
}
}
答案 1 :(得分:1)
在关机时,您的代码中存在潜在的锁定。可以进行以下交错:
main() thread thread in run() check continue_, see it is true set continue_ = false notify the condition variable join wait on condition variable
为了避免这种情况,你需要条件检查和cv等待以原子方式发生。使用continue_
(Live at Coliru)保护mutex_
最容易实现这一点:
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_;
};