简体   繁体   English

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

[英]Simplest way to make std::thread exception safe

std::thread class is inherently exception-unsafe since its destructor calls std::terminate . std::thread类本质上是异常不安全的,因为它的析构函数调用std::terminate

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

You could, of course, put everything in between construction and join() in a try-catch block, but this can get tedious and error-prone if you know you want to join or detach no matter what happens. 当然,您可以将构造和join()之间的所有内容放在try-catch块中,但如果您知道要加入或分离,无论发生什么,这都会变得乏味且容易出错。

So I was thinking how would one go about writing the simplest possible wrappers around it, but that would also support other hypothetical types of threads. 所以我在想如何围绕它编写最简单的包装器,但这也会支持其他假设类型的线程。 For instance, boost::thread or something completely different, as long as it had joinable() , join() and detach() methods. 例如, boost::thread或者完全不同的东西,只要它有joinable()join()detach()方法。 Here's how far I've got: 这是我有多远:

// 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();
}

This seems to work, but I'm wondering if there is a way to avoid manually defining the constructors and the move assignment operator. 这似乎有效,但我想知道是否有办法避免手动定义构造函数和移动赋值运算符。 Probably the biggest issue I noticed is that compilation will fail if you supply a class with deleted move constructor as a template argument. 可能我注意到的最大问题是如果你提供一个带有删除的移动构造函数的类作为模板参数,编译将会失败。

Do you have any suggestions about how to possibly avoid this? 您对如何避免这种情况有什么建议吗? Or are there other, bigger issues with this approach? 或者这种方法还有其他更大的问题吗?

If you want proper exception handling with asynchronous tasks, maybe you should use std::future rather than std::thread . 如果你想要使用异步任务进行适当的异常处理,也许你应该使用std::future而不是std::thread Instead of using join() , you'd use get() on the future, and if the future threw an exception, then get() will result in the same exception. 您将在未来使用get()而不是使用join() ,如果将来抛出异常,则get()将导致相同的异常。

A simple example: 一个简单的例子:

#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;
}

See also: 也可以看看:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM