[C++ 11] thread namespace for multithreaded programming - this_thread

In C++11, not only the thread class is added, but also a namespace STD:: this about threads is added_ Thread, which provides four common member functions in this namespace. Through these member functions, you can perform relevant operations on the current thread.

1. get_id()

Calling namespace STD:: this_ Get in thread_ The ID () method can get the thread ID of the current thread. The function prototype is as follows:

thread::id get_id() noexcept;

The corresponding example code for function usage is as follows:

#include <iostream>
#include <thread>
using namespace std;

void func()
{
    cout << "Child thread: " << this_thread::get_id() << endl;
}

int main()
{
    cout << "Main thread: " << this_thread::get_id() << endl;
    thread t(func);
    t.join();
}

When the program starts, it starts to execute the main() function. At this time, there is only one thread, that is, the main thread. After the sub thread object t is created, the specified function func() will be executed in the sub thread. In this case, call this_thread::get_id() to get the thread ID of the current thread.

2. sleep_ After the for () process is created, there are five states

After a process is created, there are five states. Similarly, after a thread is created, there are also five states: create state, ready state, running state, blocking state (suspended state) and exit state (terminated state). The transition between States is the same. Please refer to the process for more details.
There are many similarities between the execution of threads and processes. Multiple threads started in the computer need to occupy CPU resources, but the number of CPUs is limited, and each CPU can not process multiple tasks at the same time. In order to realize concurrent processing, multiple threads are time-sharing and reusing CPU time slices to quickly alternately process the tasks in each thread. Therefore, multiple threads need to compete for CPU time slice, grab it, execute it, and cannot execute if they can't grab it (because all the threads of the default priority are the same, and the kernel will also schedule from it, and there will not be a thread that will never grab CPU time slice).

Namespace this_ A sleep function sleep is provided in thread_ For(), the thread calling this function will immediately change from running state to blocking state and sleep for a certain period of time in this state. Because the blocked thread has given up CPU resources and the code will not be executed, there is no burden on the CPU during thread sleep. The function prototype is as follows. The parameter needs to specify a sleep duration, which is a time period:

template <class Rep, class Period>
  void sleep_for (const chrono::duration<Rep,Period>& rel_time);

The sample program is as follows:

#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

void func()
{
    for (int i = 0; i < 10; ++i)
    {
        this_thread::sleep_for(chrono::seconds(1));
        cout << "Child thread: " << this_thread::get_id() << ", i = " << i << endl;
    }
}

int main()
{
    thread t(func);
    t.join();
}

Compile output:

This is used in the for loop of func() function_ thread::sleep_ for(chrono::seconds(1)); After that, the program will block for 1 second every cycle, that is, the output will be carried out every 1 second. It should be noted that after the program hibernates, it will change from blocking state to ready state again. The threads in ready state need to compete for CPU time slice again. After grabbing, it will become running state, and the program will continue to run downward.

3. sleep_until()

Namespace this_ Another sleep function sleep is provided in thread_ Until (), and sleep_for() is different in its parameter types

    sleep_until(): Specifies that the thread is blocked to a specified point in time time_point Type, and then unblock
    sleep_for(): Specifies the length of time the thread is blocked duration Type, and then unblock

The function prototype of this function is as follows:

template <class Clock, class Duration>
  void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);

The sample program is as follows:

#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

void func()
{
    for (int i = 0; i < 10; ++i)
    {
        // Get the current system time point
        auto now = chrono::system_clock::now();
        // The time interval is 2s
        chrono::seconds sec(2);
        // Sleep for two seconds after the current point in time
        this_thread::sleep_until(now + sec);
        cout << "Child thread: " << this_thread::get_id() << ", i = " << i << endl;
    }
}

int main()
{
    thread t(func);
    t.join();
}

sleep_until() and sleep_ The function of the for () function is the same, except that the former blocks threads based on time points and the latter blocks threads based on time periods. In the process of project development, you can select the best solution according to the actual situation.

4. yield()

Namespace this_thread provides a very gentlemanly function yield(). After calling this function in the thread, the thread running in operation will voluntarily give up the CPU time slice that it has already grabbed, and finally become a ready state, so that the other threads will have a greater probability to grab the CPU time slice. When using this function, it should be noted that the thread will actively give up CPU resources after calling yield(), but the thread in ready state will immediately participate in the next round of CPU grabbing. It does not rule out that it can continue to grab CPU time slices. This is a probability problem.

void yield() noexcept;

The sample program corresponding to the function is as follows:

#include <iostream>
#include <thread>
using namespace std;

void func()
{
    for (int i = 0; i < 100000000000; ++i)
    {
        cout << "Child thread: " << this_thread::get_id() << ", i = " << i << endl;
        this_thread::yield();
    }
}

int main()
{
    thread t(func);
    thread t1(func);
    t.join();
    t1.join();
}

In the above program, executing the for loop in func() will take a lot of time. In extreme cases, if the CPU resources occupied by the current thread are not released, the tasks in other threads will not be processed, or the thread can grab the CPU time slice every time, resulting in no chance for tasks in other threads to be executed. The solution is to let the thread actively give up CPU resources every time the loop is executed, and then grab the CPU time slice with other threads again. If other threads grab the CPU time slice, they can execute the corresponding tasks.

Conclusion:

  • std::this_ The purpose of thread:: yield() is to prevent a thread from occupying CPU resources for a long time, resulting in the degradation of multithreading performance
  • std::this_thread::yield() allows the current thread to voluntarily give up the CPU resources it has grabbed, but it will continue to grab in the next round

Tags: C++ Back-end C++11

Posted on Mon, 25 Oct 2021 09:13:54 -0400 by b0gner17