使std :: thread异常安全的最简单方法

时间:2015-10-23 16:19:36

标签: c++ multithreading c++11 exception

std::thread类本质上是异常不安全的,因为它的析构函数调用std::terminate

std::thread t( function );
// do some work
// (might throw!)
t.join();

当然,您可以将构造和join()之间的所有内容放在try-catch块中,但如果您知道要加入或分离,无论发生什么,这都会变得乏味且容易出错。

所以我在想如何围绕它编写最简单的包装器,但这也会支持其他假设类型的线程。例如,boost::thread或完全不同的内容,只要它有joinable()join()detach()方法。这是我有多远:

// handles threads safely
// Acts the same as the underlying thread type, except during destruction.
// If joinable, will call join (and block!) during destruction.
// Keep in mind that any exception handling will get delayed because of that;
// it needs to wait for the thread to finish its work first.
template <class UNDERLYING_THREAD = std::thread>
class scoped_thread: public UNDERLYING_THREAD
{
public:
    typedef UNDERLYING_THREAD thread_type;

    using thread_type::thread_type;

    scoped_thread()
            : thread_type() {}

    scoped_thread( scoped_thread && other )
            : thread_type( std::move( other ) ) {}

    scoped_thread & operator = ( scoped_thread && other )
    {
        thread_type & ref = *this;
        ref = std::move( other );
        return *this;
    }

    ~scoped_thread()
    {
        if( thread_type::joinable() )
            thread_type::join();
    }
};

// handles autonomous threads safely
// Acts the same as the underlying thread type, except during destruction.
// If joinable, will call detach during destruction.
// Make sure it doesn't use any scoped resources since the thread can remain
// running after they go out of scope!
template <class UNDERLYING_THREAD = std::thread>
class free_thread
{
    // same except it calls detach();
}

这似乎有效,但我想知道是否有办法避免手动定义构造函数和移动赋值运算符。可能我注意到的最大问题是如果你提供一个带有删除的移动构造函数的类作为模板参数,编译将会失败。

您对如何避免这种情况有什么建议吗?或者这种方法还有其他更大的问题吗?

1 个答案:

答案 0 :(得分:5)

如果您希望使用异步任务进行适当的异常处理,可能应该使用std::future而不是std::thread。我们不会使用join(),而是在将来使用get(),如果将来会引发异常,那么get()将导致相同的异常。

一个简单的例子:

#include <future>
#include <iostream>

int my_future_task(int my_arg) {
    throw std::runtime_error("BAD STUFF!");
    return my_arg;
}

int main(int argc, char* argv[]) {
    auto my_future = std::async(my_future_task, 42);
    try {
        my_future.get();
    }
    catch(std::exception &e) {
        std::cout << "Caught exception: " << e.what() << std::endl;
    }
    return 0;
}

另见: