我希望在 C++11 中实现一个“可中断”线程,类似于 this answer .
我已经参数化了我的可中断类,以便它可以包装任何具有类似构造函数的线程类(用于 std::thread,但也应该适用于 boost::thread 之类的东西(不用介意 boost::thread 已经有这个功能))。这部分应该不重要。
尽管链接的答案有一些我必须纠正的问题,但它对我来说很有意义,所以我想弄清楚我哪里出了问题。
我已包含相关来源和结果。
interruptible.hpp
#include <atomic>
#include <exception>
#include <thread>
class interrupted_exception : public virtual std::exception
{
public:
char const *what() const noexcept { return "interrupted"; }
};
template <typename T>
class interruptible
{
public:
template <typename F, typename... A>
interruptible(F&& Function, A&&... Arguments) :
Interrupted(false),
Thread(
[](std::atomic_bool *Interrupted, F&& Function, A&&... Arguments)
{
LocalInterrupted = Interrupted;
Function(std::forward<A>(Arguments)...);
},
&this->Interrupted,
std::forward<F>(Function),
std::forward<A>(Arguments)...
)
{ }
void interrupt() { this->Interrupted = true; }
bool interrupted() const { return this->Interrupted; }
T *operator->() { return &this->Thread; }
static inline void check() noexcept(false)
{
if (!interruptible::LocalInterrupted)
return;
if (!interruptible::LocalInterrupted->load())
return;
throw interrupted_exception();
}
private:
static thread_local std::atomic_bool *LocalInterrupted;
std::atomic_bool Interrupted;
T Thread;
};
template <typename T>
thread_local std::atomic_bool *interruptible<T>::LocalInterrupted = nullptr;
main.cpp
#include <iostream>
#include <unistd.h>
#include <thread>
#include "interruptible.hpp"
void DoStuff()
{
try
{
while (true)
{
std::cout << "Loop" << std::endl;
sleep(1);
interruptible<std::thread>::check();
}
}
catch (interrupted_exception const &e)
{
std::cout << "Interrupted!" << std::endl;
}
}
int main()
{
interruptible<std::thread> a(DoStuff);
sleep(2);
std::cout << "Interrupting..." << std::endl;
a.interrupt();
sleep(2);
a->join();
return 0;
}
当我用 g++ -std=c++11 main.cpp
(gcc 4.9.2) 编译它时,我得到:
/usr/include/c++/4.9.2/functional:1665:61: error: no type named ‘type’ in ‘class std::result_of<interruptible<T>::interruptible(F&&, A&& ...) [with F = void (&)(); A = {}; T = std::thread]::<lambda(std::atomic_bool*, void (&)())>(std::atomic_bool*, void (*)())>’
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/include/c++/4.9.2/functional:1695:9: error: no type named ‘type’ in ‘class std::result_of<interruptible<T>::interruptible(F&&, A&& ...) [with F = void (&)(); A = {}; T = std::thread]::<lambda(std::atomic_bool*, void (&)())>(std::atomic_bool*, void (*)())>’
_M_invoke(_Index_tuple<_Indices...>)
^
任何人都可以阐明这个问题,我们将不胜感激!
最佳答案
std::thread
存储传递给其构造函数的参数的腐烂拷贝。对函数的引用会经历函数到指针的衰减,这意味着 DoStuff
传递给 lambda 的实际类型参数是 void(*)()
,而其 operator()
仍然期望衰减发生之前类型的参数,即 void(&)()
(推导F
)。对于 A
中的任何其他参数都会发生同样的问题。一旦您尝试使用它们,就立即参数包。
传递包裹在 std::ref
中的对象在这种情况下不是一个选项,因为引用包装器可能比存储它的实例的生命周期长(您实际上不知道 Arguments
和 Function
是什么,这些可能是纯右值临时对象,其生命周期在调用interruptible
的构造函数)。有人决定将某些实例包装在 std::ref
中可能是合理的。 在调用构造函数时 interruptible
,当人们知道某个对象的生命周期时,但这里不是这种情况。
如果您想最大限度地减少从 interruptible
传输参数所需的拷贝和移动次数到 lambda 表达式,然后进一步到 Function
对象 - 一个选项是重写构造函数,如下所示:
#include <type_traits>
// ...
template <typename F, typename... A>
interruptible(F&& Function, A&&... Arguments) :
Interrupted(false),
Thread(
[](std::atomic_bool *Interrupted,
typename std::decay<F>::type&& Function,
typename std::decay<A>::type&&... Arguments)
{
LocalInterrupted = Interrupted;
Function(std::move(Arguments)...);
},
&this->Interrupted,
std::forward<F>(Function),
std::forward<A>(Arguments)...
)
{ }
关于c++ - 如何将函数和参数包转发到 lambda、std::thread 的构造函数中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27751967/