我遵循这里的想法https://stackoverflow.com/a/13893381/1324674试图创建一个线程类,该线程类将具有一个指向线程退出(或者更确切地说,抛出异常并结束)的布尔标志的静态指针。我有几个问题,但是在遵循相同的代码并且我想知道如何修复它时,我已经在这几个小时了 -
using namespace std;
class InterruptThreadException {};
class InterruptibleThread {
private:
static thread_local atomic_bool* flagRef;
atomic_bool flag{false};
thread thrd;
public:
friend void checkForInterrupt( );
template < typename Function, typename... Args >
InterruptibleThread( Function&& _fxn, Args&&... _args )
: thrd(
[]( atomic_bool& f, Function&& fxn, Args&&... args ) {
flagRef = &f;
fxn( forward< Args >( args )... );
},
flag,
forward< Function >( _fxn ),
forward< Args >( _args )...
) {
thrd.detach( );
}
bool stopping( ) const {
return flag.load( );
}
void stop( ) {
flag.store( true );
}
};
thread_local atomic_bool* InterruptibleThread::flagRef = nullptr;
void checkForInterrupt( ) {
if ( !InterruptibleThread::flagRef ) {
return;
}
if ( !InterruptibleThread::flagRef->load( ) ) {
return;
}
throw InterruptThreadException( );
}
void doWork( ) {
int i = 0;
while ( true ) {
cout << "Checking for interrupt: " << i++ << endl;
checkForInterrupt( );
this_thread::sleep_for( chrono::seconds( 1 ) );
}
}
int main( ) {
InterruptibleThread t( doWork );
cout << "Press enter to stop\n";
getchar( );
t.stop( );
cout << "Press enter to exit" << endl;
getchar( );
return 0;
}
现在我相信这个问题来自于线程调用中的lambda函数,好像我删除了它,只是让fxn和args调用程序运行正常(显然没有终止线程的能力)。
知道我做错了吗?
以下错误 - visual studio:
Error C2672 'std::invoke': no matching overloaded function found
Error C2893 Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...) noexcept(<expr>)'
GNU: https://gist.github.com/anonymous/2d5ccfa9ab04320902f51d005a96d2ae
答案 0 :(得分:4)
您的问题是std::thread
存储了其参数的副本,因此Function
的类型和传递给lambda的对象类型不相同。
当你致电InterruptibleThread(doWork)
时,Function
被推断为void (&)()
:也就是说,引用一个不带参数的函数并返回void。当您将fxn
传递给std::thread
的构造函数时,它会衰减到void (*)()
(指向函数的指针,不带参数并返回void)并存储在某个内部存储中。当新线程启动时,内部对象作为第二个参数传递给lambda,但类型void (*)()
与预期的void (&)()
不匹配,一切都崩溃了。同样地,如果我将一个左值引用传递给某个仿函数,那么你的构造函数当前不起作用。
args
存在类似的问题。例如,如果您将左值std::string
作为args
之一传递,它将作为std::string&&
传递给lambda,并且会爆炸。
解决此问题的最简单方法是使lambda参数类型为auto
(假设您正在使用c ++ 14):
InterruptibleThread( Function&& fxn, Args&&... args )
: thrd(
[]( atomic_bool& f, auto&& fxn, auto&&... args ) {
flagRef = &f;
fxn( std::move(args)... );
},
std::ref(flag),
std::forward<Function>(fxn),
std::forward<Args>(args)... ) {
}
如果您仍然使用c ++ 11,则必须使用std::decay
来获取正确的参数类型:
InterruptibleThread( Function&& fxn, Args&&... args )
: thrd(
[]( atomic_bool& f,
typename std::decay<Function>::type&& fxn,
typename std::decay<Args>::type&&... args ) {
flagRef = &f;
fxn( std::move(args)... );
},
std::ref(flag),
std::forward<Function>(fxn),
std::forward<Args>(args)... ) {
}
请注意,在flag
中我std::reference_wrapper
包裹了std::atomic
,因为template <typename T>
class Tab {
public:
Tab(std::size_t length) : tab(length) {}
// ...
private:
std::vector<T> tab;
};
boost::variant<Tab<int>, Tab<double> >
CreateTab(const std::string& type, std::size_t length)
{
if (type == "int") {
return Tab<int>(length);
} else if (type == "double") {
return Tab<double>(length);
}
throw std::runtime_error("Bad type");
}
不可复制,即使是复制也不正确。