C ++ 11 multithreading-Basic use
C ++ 11 multithreading-Basic use
This article is just a simple record of my own, and the content is relatively basic for the children's shoes who have never used the c ++ 11 thread library.
-1. Basic thread usage
-2. mutex
-3. Condition Variables
-4. atomic variables
1. Basic use of threads Code:
# Include
# Include
# Include
# Include
Int k = 0; void fun (void) {// thread sleep, chrono is the time-related database of c ++ 11. Std: this_thread: sleep_for (std: chrono: seconds (3); for (int I = 0; I <10; ++ I) {std :: cout
Note:
1. in linux, gcc or clang must be added with-pthread to connect to the thread Library; otherwise, an error will occur.
2. The main thread function cannot end the newly created thread function in advance, because in c ++ 11, the thread is also an object, and the main function end thread object is destroyed.
3. t. join () is the main function blocking wait until the end of the thread to end, the main function will continue to execute, and blocking at the return
T. detach () The main function and the thread function are separated, each executes its own, and the thread becomes the background thread.
4. You can create a thread through bind and lambda.
The thread can be stored in the container to ensure the Declaration cycle of the thread object.
Note that the thread does not copy the constructor, and there is a moving constructor.
As shown in the figure, the copy constructor is delete.
2. mutex
There are four types
Std: mutex exclusive mutex, which cannot be used recursively
Std: timed_mutex exclusive mutex with timeout, which cannot be used recursively
Std: recursive_mutex recursive mutex, without timeout
Std: recursive_timed_mutex recursive mutex with timeout
Code:
# Include
# Include
# Include
# Include
# Include
Std: mutex g_lock; int I = 0; void func (void) {// use the RAII method to automatically release std: lock_guard when leaving the scope
Locker (g_lock); // normal mutex lock // g_lock.lock (); I ++; std: cout <I <std: endl; // unlock the mutex lock // g_lock.unlock ();} int main (int argc, char * argv []) {std: thread t1 (func); std :: thread t2 (func); std: thread t3 (func); t1.join (); t2.join (); t3.join (); return EXIT_SUCCESS ;}
Note:
1. multiple attempts to obtain mutex may cause deadlocks. Therefore, we call the std: recursive_mutex recursive lock to allow the lock to be obtained multiple times by the same thread. Recursive locks are generally not recommended. Cause: <1. the use of recursive locks will complicate the logic of the program, and the use of recursive locks can be simplified. <2. Recursive locks are less efficient than non-recursive locks. <3. The number of reentrant recursive locks is limited. If the number of reentrant attempts exceeds the limit, an error is returned.
2. You can use a mutex lock with a time-out period to avoid blocking and waiting for the mutex lock.
3. unique_lock: it is a common mutex encapsulation class. Unlike lock_guard, it also supports latency lock, time lock, recursive lock, transfer of lock ownership, and the use of conditional variables. This is also a non-replicable class, but it can be moved.
3. Condition Variables
Blocking one or more threads will not wake up the current blocked process until the notification or timeout from another thread is received.
Condition variables must be used with mutex
C ++ 11 provides two conditional variables
1. std: condition_variable, used with std: unique_lock for wait operations
2. std: condition_variable_any, which can be used with any mutex with lock and unlock. It is flexible but inefficient.
Wait of the condition variable also has an overload method. You can set a condition. The condition variable first checks whether the condition is met by the condition type.
Principle:
When a wait function of the std: condition_variable object is called, it uses std: unique_lock (through std: mutex) to lock the current thread. The current thread will be blocked until another thread calls the notification function on the same std: condition_variable object to wake up the current thread.
Code: use c ++ 11 multi-thread to implement synchronous queue
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
Template
Class SynQueue {public: // constructor SynQueue (int MaxSize): m_maxsize (MaxSize) {}// Put the T type object into the queue void Put (const T & x) {std:: lock_guard
Locker (m_mutex); while (isFull () {// if it is full, wait for m_notFull.wait (m_mutex);} m_queue.push_back (x); // wake up a thread through the condition variable, all threads can also m_notempty.policy_one () ;}// extract the T-type object from the queue void Take (T & x) {std: lock_guard
Locker (m_mutex); while (isEmpty () {std: cout <no resource... please wait <std: endl; m_notEmpty.wait (m_mutex);} x = m_queue.front (); m_queue.pop_front (); m_notfull.policy_one ();} // determine whether the queue is Empty bool Empty () {std: lock_guard
Locker (m_mutex); return m_queue.empty ();} // checks whether the queue is Full bool Full () {std: lock_guard
Locker (m_mutex); return m_queue.size () = m_maxsize;} // return queue Size size_t Size () {std: lock_guard
Locker (m_mutex); return m_queue.size ();} private: // determines whether it is null or full. You do not need to lock bool isFull () const {return m_queue.size () = m_maxsize ;} bool isEmpty () const {return m_queue.empty ();} private: // queue std: list
M_queue; // mutex std: mutex m_mutex; // condition variable std: condition_variable_any m_notEmpty when the lock is not empty; // condition variable std :: condition_variable_any m_notFull; // maximum queue length int m_maxsize ;}; void func (SynQueue
* Sq) {int ret; sq-> Take (ret); std: cout <ret <std: endl;} int main (int argc, char * argv []) {// creates a thread queue with a maximum length of 20 SynQueue
Syn (20); // place the data object for (int I = 0; I <10; I ++) {syn. put (I);} std: cout <syn. size () <std: endl; // The thread cannot be copied. containers and smart pointers are used to manage the thread survival std: vector.
> Tvec; // multiple cycles are performed once. If the resources are insufficient, the last thread is blocked and a resource is added to the backend to check whether the thread is wakened and executed. For (int I = 0; I <11; I ++) {// create a thread and save the smart pointer of the Management thread to the tvec. push_back (std: make_shared
(Func, & syn); // change to the background thread tvec [I]-> detach ();} sleep (10); // Add a resource syn. put (11); sleep (10); return EXIT_SUCCESS ;}
Running result:
4. atomic variables
Atomic variable, which is an atomic operation and does not need to be locked
std::atomic
For more information, see. Here is a simple example.
Cppreference atomic
Code:
# Include
# Include
# Include
# Include
# Include
// Create an int-type atomic variable std: atomic
Atc (0); void func () {std: cout <atc <std: endl; atomic variable auto-incrementing atc ++;} int main (int argc, char * argv []) {std: vector
Tvec; for (int I = 0; I <10; I ++) {std: thread t (func); // thread object moving semantics tvec. push_back (std: move (t); tvec [I]. join ();} return EXIT_SUCCESS ;}