Valid tive Modern C ++ Item 37: ensure that std: thread is unjoinable during destruction, stdunjoinable

Source: Internet
Author: User

Valid tive Modern C ++ Item 37: ensure that std: thread is unjoinable during destruction, stdunjoinable

In the following code, if func is called, the program will be terminated (std: terminate) according to the C ++ standard)

void func(){    std::thread t([]    {        std::chrono::microseconds dua(1000);        std::this_thread::sleep_for(dua);    });}

The reason is that the std: thread must be unjoinable when the analysis structure of std: thread is called in the C ++ standard. Otherwise, std: terminate will be called.

Std: thread has two states: joinable and unjoinable. The std: thread of unjoinable includes:

  • Use the default std: thread. This std: thread does not execute any tasks.
  • Moved std: thread. For example, std: thread t2 (std: move (t1), the execution of t1 is transferred to t2, and t1 is changed to the unjoinable state.
  • Std: thread that has been joined. After the join operation is called, std: thread changes to the unjoinable state.
  • Std: thread that has been detach. Detach disconnects std: thread from the execution task.

The thread created in the previous func is in the joinable state during destruction (not the Default Construction, not moved, no join and detach, And the thread is still running ), therefore, according to the C ++ standard, the program will be forcibly terminated.

Why should C ++ adopt this method of violence? This is because other methods may cause problems.

Let's assume that the C ++ standard uses other methods to analyze the problems:

1. explicitly call join in the analysis structure of std: thread. This method may cause potential performance problems because join is a blocking call, which means that the thread structure may be blocked. In some cases, thread join is not expected, join is performed only when certain conditions are met, such as the following code:

void doSomething(){    std::thread t(doWork());     if(someCondition())    {        t.join();        getResult();    }}

The intention of the code is to join the thread only when the condition is met. If the condition is not met, the code will be returned directly. Because explicit join is performed in the analysis structure of std: thread, even if the condition is not satisfied, the join operation is performed when the function exits. If doWork is a time-consuming step, doSomething will be blocked until doWork is completed regardless of the full condition.

2. explicitly call detach in the analysis structure of std: thread. This method does not seem to have the first performance problem. In fact, it is worse and may cause a runtime error. For example, the following code:

void doSomething(){    std::vector<int> data;    std::thread t([&data]    {        for (int i = 0; i <= 1000; ++i)            data.push_back(i);    });}

When the function exits, std: thread calls detach, the thread execution task continues, and the temporary variable of the function Stack has been destroyed, and the program will be undefined, debugging is also very difficult. Detach itself is prone to bugs, so this method cannot be used.

Because the above two methods have problems, C ++ adopts the method of brute force program termination. In fact, C ++ forces programmers to ensure std :: the thread is destroyed correctly. Otherwise, your program will be killed. This is the philosophy of C ++. Other languages do not necessarily use this method for this issue.

Meyers recommends "Make std: threads unjoinable on all paths", that is, making std: thread unjoinable during destruction. This is a trade-off option, which is similar to the first one that may cause potential performance problems. However, compared to the other two options: Program termination; detach's undefined behavior, this is acceptable (for performance problems, you can compensate by implementing interruptible threads ).

To ensure "Make std: threads unjoinable on all paths", when the function returns and an exception occurs, the thread is in the unjoinable state, so you can use RAII to complete it:

class ThreadRAII{public:    enum class DtorAction { join, detach };public:    ThreadRAII(std::thread&& t, DtorAction act)        :m_action(act), m_thread(std::move(t)) {}    ~ThreadRAII()    {        if (m_thread.joinable())        {            if (m_action == DtorAction::join)                m_thread.join();            else                m_thread.detach();        }    }    ThreadRAII(ThreadRAII&&) = default;    ThreadRAII& operator=(ThreadRAII&&) = default;    std::thread& get() { return m_thread; }private:    std::thread m_thread;    DtorAction m_action;};

The ThreadRAII constructor receives std: thread rvalue because std: thread cannot be copied. After the move operation is called, The passed std: thread becomes unjoinable, the task is transferred to the std: thread of ThreadRAII.

With ThreadRAII, you can safely use std: thread:

void doSomething(){    ThreadRAII t(std::thread([]    {       ....    }    ));    if(someCondition())    {        t.get().join();        ...    }}

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.