C++ Concurrency Guide std::mutex

These articles are all notes recorded after reading the Great God, link https://www.cnblogs.com/haippy/p/3284540.html

Mutex is also called mutex. In C++11, Mutex-related classes (including lock types) and functions are declared in the <mutex> header file, so if you need to use std::mutex, you must include the <mutex> header file.

<mutex> header file

Mutex series

  • std::mutex, the most basic Mutex class.
  • std::recursive_mutex, the recursive Mutex class.
  • std::time_mutex, the timing Mutex class.
  • std::recursive_timed_mutex, timed recursive Mutex class.

Lock class

  • std::lock_guard, related to Mutex RAII, facilitates threads to lock the mutex.
  • std::unique_lock, related to Mutex RAII, facilitates threads to lock mutex, but provides better lock and unlock control.

Other types

  • std::once_flag
  • std::adopt_lock_t
  • std::defer_lock_t
  • std::try_to_lock_t

function

  • std::try_lock, try to lock multiple mutexes at the same time.
  • std::lock can lock multiple mutexes at the same time.
  • std::call_once, if multiple threads need to call a function at the same time, call_once can ensure that multiple threads call the function only once.

std::mutex

std::mutex is the most basic mutex in C++11. The std::mutex object provides the characteristic of exclusive ownership—that is, it does not support recursively locking std::mutex objects, while std::recursive_lock The mutex object can be locked recursively.

  • The constructor, std::mutex does not allow copy construction, nor move copy. The initially generated mutex object is in the unlocked state.
  • lock(), the calling thread will lock the mutex. When the thread calls this function, the following three situations will occur: (1). If the mutex is not currently locked, the calling thread locks the mutex, and the thread has the lock until unlock is called. (2). If the current mutex is locked by other threads, the current calling thread is blocked. (3). If the current mutex is locked by the current calling thread, a deadlock (deadlock) will occur.
  • unlock(), unlock, release the ownership of the mutex.
  • try_lock(), try to lock the mutex, if the mutex is occupied by other threads, the current thread will not be blocked. The following 3 situations also occur when a thread calls this function, (1). If the current mutex is not occupied by other threads, the thread locks the mutex until the thread calls unlock to release the mutex. (2). If the current mutex is locked by other threads, the current calling thread returns false without being blocked. (3). If the current mutex is locked by the current calling thread, a deadlock (deadlock) will occur.

for example:

#include <iostream>         // std::cout
#include <thread>            // std::thread
#include <mutex>            // std::mutex

volatile int counter(0);     // non-atomic counter
std::mutex mtx;                // locks access to counter

void attempt_10k_increases() {
    
    
    for (int i=0; i<10000; ++i) {
    
    
        if (mtx.try_lock()) {
    
                // only increase if currently not locked:
            ++counter;
            mtx.unlock();
        }
    }
}

int main () {
    
    
    std::thread threads[10];
    for (int i=0; i<10; ++i) {
    
    
		threads[i] = std::thread(attempt_10k_increases);
	}
        
    for (auto& th : threads) {
    
    
		th.join();
	}
    std::cout << counter << " successful increases of the counter.\n";

    return 0;
}

std::recursive_mutex

std::recursive_mutex, like std::mutex, is also an object that can be locked, but unlike std::mutex, std::recursive_mutex allows the same thread to lock the mutex multiple times (that is, recursive Lock) to obtain multiple levels of ownership of the mutex object. When std::recursive_mutex releases the mutex, it is necessary to call unlock() the same number of times as the depth of the lock level, which can be understood as the number of lock() and unlock() The number of times is the same, except that the characteristics of std::recursive_mutex are roughly the same as std::mutex.

std::time_mutex

std::time_mutex has two more member functions than std::mutex, try_lock_for(), try_lock_until().

  • The try_lock_for function accepts a time range, which means that the thread will be blocked if it does not acquire the lock within this period of time (different from the try_lock() of std::mutex, try_lock will directly return false if the lock is not acquired when it is called), If other threads release the lock during this period, the thread can acquire the lock on the mutex. If it times out (that is, the lock is not acquired within the specified time), false is returned.

  • The try_lock_until function accepts a point in time as a parameter. Before the specified point in time, the thread is blocked if it does not acquire the lock. If other threads release the lock during this period, the thread can acquire the lock on the mutex. If Overtime (that is, the lock is not obtained within the specified time), then false is returned.

The following small example illustrates the usage of std::time_mutex:

#include <iostream>       // std::cout
#include <chrono>         // std::chrono::milliseconds
#include <thread>          // std::thread
#include <mutex>          // std::timed_mutex

std::timed_mutex mtx;

void fireworks() {
    
    
	// waiting to get a lock: each thread prints "-" every 200ms:
	while (!mtx.try_lock_for(std::chrono::milliseconds(200))) {
    
    
		std::cout << "-";
	}
	// got a lock! - wait for 1s, then this thread prints "*"
	std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	std::cout << "*\n";
	mtx.unlock();
}

int main ()
{
    
    
	std::thread threads[10];
	// spawn 10 threads:
	for (int i=0; i<10; ++i) {
    
    
		threads[i] = std::thread(fireworks);
	}


	for (auto& th : threads) {
    
    
		th.join();
	}

	return 0;
}

std::recursive_timed_mutex

Like the relationship between std:recursive_mutex and std::mutex, the characteristics of std::recursive_timed_mutex can also be derived from std::timed_mutex, and you can check the information yourself.

std::lock_guard

Related to Mutex RAII, it is convenient for threads to lock the mutex.

#include <iostream>        // std::cout
#include <thread>           // std::thread
#include <mutex>           // std::mutex, std::lock_guard
#include <stdexcept>     // std::logic_error

std::mutex mtx;

void print_even (int x) {
    
    
    if (x%2==0) std::cout << x << " is even\n";
    else throw (std::logic_error("not even"));
}

void print_thread_id (int id) {
    
    
    try {
    
    
        // using a local lock_guard to lock mtx guarantees unlocking on destruction / exception:
        std::lock_guard<std::mutex> lck (mtx);
        print_even(id);
    }
    catch (std::logic_error&) {
    
    
        std::cout << "[exception caught]\n";
    }
}

int main ()
{
    
    
    std::thread threads[10];
    // spawn 10 threads:
    for (int i=0; i<10; ++i)
        threads[i] = std::thread(print_thread_id,i+1);

    for (auto& th : threads) th.join();

    return 0;
}

std::unique_lock

Similar to std::lock_guard. There are detailed instructions in another article.

#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock

std::mutex mtx;           // mutex for critical section

void print_block (int n, char c) {
    
    
    // critical section (exclusive access to std::cout signaled by lifetime of lck):
    std::unique_lock<std::mutex> lck (mtx);
    for (int i=0; i<n; ++i) {
    
    
        std::cout << c;
    }
    std::cout << '\n';
}

int main ()
{
    
    
    std::thread th1 (print_block,50,'*');
    std::thread th2 (print_block,50,'$');

    th1.join();
    th2.join();

    return 0;
}

Guess you like

Origin blog.csdn.net/qq_24649627/article/details/113727305