The predecessor of the STL thread library is boost: thread. After C ++ 11 standardization, it is formally summarized into the stl library. Through it, we can easily implement cross-platform thread management.
Thread Management
In the std: thread library, a thread is represented by a thread object. When a thread object is created, a thread is created. A simple example is as follows:
# Include
<Iostream>
# Include
<Thread>
Using
Namespace std;
Void thread_entry (const
Char * arg)
{
Cout <"thread" <this_thread: get_id () <"created:" <arg <endl;
}
Int main ()
{
Thread
Thrd (thread_entry, "hello world ");
Thrd. join ();
Return 0;
}
Here I create a thread through the thread object and call the join function to wait until the thread ends. Thead object constructor can input parameters, which is very convenient.
Std: this_thread namespace
In the previous example, A this_thread: get_id () function is used to obtain the tid of the current thread. The std: this_thread namespace provides the following functions to manage the current thread:
- Yield
- Get_id
- Sleep_for
- Sleep_until
You can basically guess their functions from their names. Taking the sleep_for function as an example, it provides a cross-platform sleep function and no longer needs to be encapsulated by itself:
Std: chrono: milliseconds dura (2000 );
Std: this_thread: sleep_for (dura );
PS: when using this function in gcc, you need to add the-D_GLIBCXX_USE_NANOSLEEP option when compiling again. Otherwise, a syntax error is reported. For details, refer to this article.
Linux platform running error
The above code runs well on the Windows platform, but the following error is found when running on Linux:
Tianfang> run
Terminate called after throwing an instance of 'std: system_error'
What (): Operation not permitted
Aborted
Tianfang>
Then I found the answer on StackOverFlow: add the-pthread option during the link. This should be a bug.
Delete a Thread object
I initially thought that when the thread object is deleted, it will automatically kill the thread, but I found that when the thread object is deleted, if the join or attach is not called, an exception will be reported. To demonstrate this process, we first change the main function to the following:
Int main ()
{
Thread thrd (thread_entry, "hello world ");
// Thrd. join ();
Return 0;
}
When running this code, you will find the following error:
Tianfang> run
Terminate called without an active exception
Aborted
Tianfang>
That is to say, the thread object does not automatically manage the end of the thread and needs to be manually controlled. There are two common control methods: join and detach. Join has been introduced. It is used to wait for the thread to end. The detach function is to host the thread, and the thread continues to run until the end, but is no longer controlled by the thread object.
People familiar with the thread programming model may find that it does not provide an option to force terminate a thread. The this_thread namespace in boost actually provides the function for terminating the thread, but it is not included in stl. It can be seen that the Standards Committee does not recommend force terminating this kind of robustness.
Mutual Exclusion and Synchronization
Mutex
In stl, mutex is divided into four objects based on recursion and Timeout:
- Mutex
- Timed_mutex
- Recursive_mutex
- Recursive_timed_mutex
The operation functions of these four objects are basically the following:
- Lock
- Trylock try to get the lock
- Unlock release lock
- Try_lock_for tries to obtain the lock within a certain period of time (mutex with the timeout function can be used)
- Try_lock_until try to get the lock to a certain point in time (mutex with timeout function can be used)
In addition to using these classes directly, you can also use encapsulation classes such as lock_guar and unique_lock. Its functions are similar to the lock keyword of C # (the lock range is not equivalent ), the lock is automatically obtained within the specified range and is automatically unlocked out of the range. This effectively prevents Asymmetric Addition and unlocking.
In addition, stl provides the generic methods of try_lock and lock to implement batch lock acquisition. We will not discuss them here.
Condition variable
In stl, conditional variables are encapsulated in the condition_variable class. The basic usage of conditional variables is similar to that of system APIs. The following is an example of a producer/consumer:
# Include
<Condition_variable>
# Include
<Mutex>
# Include
<Thread>
# Include
<Iostream>
# Include
<Queue>
# Include
<Chrono>
Using
Namespace std;
Int main ()
{
Queue <int> buffer;
Mutex m;
Condition_variable cond_var;
Int num = 0;
Thread producer ([&] ()
{
While (true)
{
This_thread: sleep_for (chrono: seconds (1 ));
Unique_lock <std: mutex> lock (m );
Num ++;
Std: cout <"producing" <num <'\ n ';
Buffer. push (num );
Cond_var.policy_one ();
}
});
Thread consumer ([&] ()
{
While (true)
{
Unique_lock <std: mutex> lock (m );
If (buffer. empty ())
Cond_var.wait (lock );
Std: cout <"consuming" <buffer. front () <'\ n ';
Buffer. pop ();
}
});
Producer. join ();
Consumer. join ();
}
Conditional variables have several other packages, so we will not introduce them here.
Others
Several other classes that are mutually exclusive to synchronization, such as barriers and read/write locks, are not included in STL. If you want to use them, you can directly use the boost library.