我编写了一个超级简单的线程包装器,该包装器具有一个函数并将其运行在线程中,并提供了一种简单的机制来在需要退出时向线程发出信号。启动功能看起来像
//tw.hpp
class ThreadWrapper
{
public:
// ...snipped ...
template<typename... Args>
bool start(Args&& ... args)
{
ft_ = std::async(std::launch::async, std::forward<Args>(args)... );
return true;
}
};
当我将其用于非成员函数时,我需要将包装器的const ref传递到正在运行的函数中,以提供一个该函数可用来知道何时退出的句柄:
void lone_worker(const ThreadWrapper& tw)
{
while (!tw.is_quit_requested())
{
std::cout << "working hard alone\n";
sleep(1);
}
}
void nonmember_demo()
{
ThreadWrapper tw;
tw.start(&lone_worker, std::cref(tw)); // the cref is need to avoid hundreds of lines of compiler template puke with no useful error messages
sleep(5);
std::cout << "quitting\n";
tw.request_quit();
}
我最初不使用std::cref
进行编译时就措手不及,实际上是数百行编译器模板puke(gcc 8.1.0)并没有明确的原因。是否有某些我需要完善的转发功能才能使用cref的功能?我认为部分原因是该类不可复制(它包含一个std :: future),这种气味有点儿,因为至少我认为一开始就不应复制任何内容。
完整示例如下:https://coliru.stacked-crooked.com/a/0eb4d6160b44764a
答案 0 :(得分:1)
闻起来有点香,因为至少我认为一开始就不应该复制任何内容
您假设错误。 async
主要只是转发到thread
,其开始于执行:
std::invoke(decay_copy(std::forward<Function>(f)), decay_copy(std::forward<Args>(args))...);
这确实复制了所有参数。参考包装的重点是避免这种复制-您正在复制ThreadWrapper
(可复制),而不是复制std::reference_wrapper<ThreadWrapper const>
对象(不可复制)。
从链接的thread
的cppreference页面中:
线程函数的参数按值移动或复制。如果需要将引用参数传递给线程函数,则必须将其包装(例如,使用
std::ref
或std::cref
)。