C++11 介绍 mutex(互斥)
2014-01-19
首先看mutex头文件里面有的内容,如下表:
| Classes | |
| Mutexes | |
| mutex | Mutex class (class ) |
| recursive_mutex | Recursive mutex class (class ) |
| timed_mutex | Timed mutex class (class ) |
| recursive_timed_mutex | Recursive timed mutex (class ) |
| Locks | |
| lock_guard | Lock guard (class template ) |
| unique_lock | Unique lock (class template ) |
| Other types | |
| once_flag | Flag argument type for call_once (class ) |
| adopt_lock_t | Type of adopt_lock (class ) |
| defer_lock_t | Type of defer_lock (class ) |
| try_to_lock_t | Type of try_to_lock (class ) |
| Functions | |
| try_lock | Try to lock multiple mutexes (function template ) |
| lock | Lock multiple mutexes (function template ) |
| call_once | Call function once (public member function ) |
---------------------------------------------------------------------------
mutex类
Mutex class
A mutex is a lockable object that is designed to signal when critical sections of code need exclusive access, preventing other threads with the same protection from executing concurrently and access the same memory locations.
mutex objects provide exclusive ownership and do not support recursivity (i.e., a thread shall not lock a mutex it already owns) -- see recursive_mutex for an alternative class that does.
It is guaranteed to be a standard-layout class.
| (constructor) | Construct mutex (public member function ) |
| lock | Lock mutex (public member function ) |
| try_lock | Lock mutex if not locked (public member function ) |
| unlock | Unlock mutex (public member function ) |
| native_handle | Get native handle (public member function ) |
从Menber functions说起:
(constructor) 构造函数 负责线程对象的初始化构造。
lock 锁互斥变量。
try_lock 尝试锁互斥变量。
unlock 解锁互斥变量。
native_handle 返回当前句柄,句柄指的是一个核心对象在某一个进程中的唯一索引。
#include <iostream> #include <thread> #include <mutex> #include <windows.h> using namespace std; mutex mtx; // 定义了一个互斥量 mtx // -------------lock()----------------------- void print_block(int n, char c) { // 定义了一个临界区,意味着mtx上了锁,独占访问(单独占有这个区域,并且只有此区域可以访问区域内的数据) mtx.lock(); for (int i = 0; i<n; ++i) { std::cout << c; } cout << '\n'; mtx.unlock(); } void main_print_block() { std::thread th1(print_block, 50, '*'); std::thread th2(print_block, 50, '$'); th1.join(); th2.join(); } volatile int sum(0); void attempt_20k_increases() { for (int i = 0; i<20000; ++i) { if (1) { // 上锁操作 mtx.lock(); ++sum; mtx.unlock(); } } } void main_attempt_lock() { thread threads[10]; // 产生10个线程 for (int i = 0; i<10; ++i) threads[i] = thread(attempt_20k_increases); for (auto& th : threads) th.join(); cout << sum << " successful increases of the counter.\n"; } // -------------try_lock()----------------------- // ----如果能上锁,就执行区域内代码,否则pass-- void print_thread_id(int id) {
sleep(50) if (mtx.try_lock()){ cout << "thread " << id << '\n'; mtx.unlock(); }
else
cout << "not running thread " << id << endl;
} void main_lock() { thread threads[10]; for (int i = 0; i<10; ++i) threads[i] = thread(print_thread_id, i + 1); for (auto& th : threads) th.join(); } volatile int counter(0); void attempt_10k_increases() { for (int i = 0; i<10000; ++i) { if (mtx.try_lock()) { ++counter; mtx.unlock(); } } } void main_attempt() { thread threads[10]; for (int i = 0; i<10; ++i) threads[i] = thread(attempt_10k_increases); for (auto& th : threads) th.join(); cout << counter << " successful increases of the counter.\n"; } void main() { cout << "演示 lock()" << endl; main_print_block(); cout << endl; cout << "演示 lock()" << endl; main_attempt_lock(); cout << endl; cout << "演示 try_lock()" << endl; main_lock(); cout << endl; cout << "演示 try_lock()" << endl; main_attempt(); cout << endl; }这个例子中显示,列出了2个关于lock(),2个关于unlock()的例子。
lock()的意思是,锁上mtx,才可以执行区域内代码。(unclear)
try_lock()的意思是,如果mtx是上锁的状态,就执行区域内代码,否则跳过此区域,执行下面顺序的代码。
代码执行结果:演示 lock()
**************************************************
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$演示 lock()
200000 successful increases of the counter.演示 try_lock()
not running thread 4
not running thread 5
not running thread 8
not running thread 9
not running thread 2
not running thread 10
not running thread 7
thread 6
not running thread 1
not running thread 3演示 try_lock()
18702 successful increases of the counter.请按任意键继续. . .
---------------------------------------------------------------------------
recursive_mutex类
Recursive mutex class
A recursive mutex is a lockable object, just like mutex, but allows the same thread to acquire multiple levels of ownership over the mutex object.
This allows to lock (or try-lock) the mutex object from a thread that is already locking it, acquiring a new level of ownership over the mutex object: the thread will remain locked until member unlock is called as many times as this level of ownership.
It is guaranteed to be a standard-layout class.
recursive_mutex 意思是递归锁类,recursive_mutex 类的对象例如 mtx ,可以上锁多次,但是解锁必须匹配上锁的次数,才能保证不死锁。
普通的mutex类的对象 mtx,不可能多次上锁。recursive_mutex 的函数功能与 mutex 基本一样。
| (constructor) | Construct recursive mutex (public member function ) |
| lock | Lock recursive mutex (public member function ) |
| try_lock | Lock recursive mutex if not locked (public member function ) |
| unlock | Unlock recursive mutex (public member function ) |
| native_handle | Get native handle (public member function ) |
从Menber functions说起:
(constructor) 构造函数 负责线程对象的初始化构造。
lock 锁互斥变量。
try_lock 尝试锁互斥变量。
unlock 解锁互斥变量。
native_handle 返回当前句柄,句柄指的是一个核心对象在某一个进程中的唯一索引。
下面这两个函数显示了recursive_mutex 类的含义。
#include <iostream> #include <thread> #include <mutex> #include <windows.h> using namespace std; volatile int counter(0); recursive_mutex mtx; mutex mtx_1; void attempt_10k_increases() { for (int i = 0; i<1; ++i) { if (1) { mtx.lock(); cout << "lock 1 time" << endl; mtx.lock(); cout << "lock 2 times" << endl; ++counter; cout << counter << endl; mtx.unlock(); cout << "unlock 1 time" << endl; mtx.unlock(); cout << "unlock 2 times" << endl; cout << "------------------------" << endl; Sleep(50); } } } void main_attempt_10k() { thread threads[3]; for (int i = 0; i<3; ++i) threads[i] = thread(attempt_10k_increases); for (auto& th : threads) th.join(); cout << counter << " successful increases of the counter.\n"; } void attempt_10k_increases_try_lock() { for (int i = 0; i<10000; ++i) { if (mtx.try_lock()) { if (mtx.try_lock()) { ++counter; mtx.unlock(); mtx.unlock(); } } } } void main_attempt_10k_try_lock() { thread threads[10]; for (int i = 0; i<10; ++i) threads[i] = thread(attempt_10k_increases_try_lock); for (auto& th : threads) th.join(); cout << counter << " successful increases of the counter.\n"; } void main() { main_attempt_10k(); cout << "\n\n" << endl; main_attempt_10k_try_lock(); }代码结果:
lock 1 time
lock 2 times
1
unlock 1 time
unlock 2 times
lock 1 time
------------------------
lock 2 times
2
unlock 1 time
unlock 2 times
------------------------
lock 1 time
lock 2 times
3
unlock 1 time
unlock 2 times
------------------------
3 successful increases of the counter.
13663 successful increases of the counter.
---------------------------------------------------------------------------
timed_mutex类
Timed mutex class
A timed mutex is a time lockable object that is designed to signal when critical sections of code need exclusive access, just like a regular mutex, but additionally supporting timed try-lock requests.
As such, a timed_mutex has two additional members: try_lock_for and try_lock_until.
It is guaranteed to be a standard-layout class.
| (constructor) | Construct timed mutex (public member function ) |
| lock | Lock timed mutex (public member function ) |
| try_lock | Lock timed mutex if not locked (public member function ) |
| unlock | Unlock timed mutex (public member function ) |
| native_handle | Get native handle (public member function ) |
| try_lock_for | Try to lock for time span (public member function ) |
| try_lock_until | Try to lock until time point (public member function ) |
从Menber functions说起:
(constructor) 构造函数 负责线程对象的初始化构造。
lock 锁互斥变量。
try_lock 尝试锁互斥变量。
try_lock_for 尝试在给定时间内lock 这个 mtx 一次
try_lock_until 尝试在给定时间点内锁这个mtx,如果锁成,就返回 true,否则 fasle
unlock 解锁互斥变量。
native_handle 返回当前句柄,句柄指的是一个核心对象在某一个进程中的唯一索引。
执行代码:
#include <iostream> #include <thread> #include <chrono> #include <mutex> #include <ctime> #include <windows.h> using namespace std; #pragma warning( disable : 4996 ) int counter = 0; mutex mmtx; timed_mutex mtx; void fireworks() { while (!mtx.try_lock_for(chrono::milliseconds(498))) //尝试在0.49s内lock 这个 mtx 一次,如果没锁成,就打印一个“-” //如果锁成,就跳出while,按顺序执行下面语句。 //和try_lock()的区别,try_lock_for()是在给定的时间内,执行一次锁动作 //try_lock()是 只要时间片转到这个线程上,就执行,可以说成无限尝试 try_lock()动作 { //while (!mmtx.try_lock()) { cout <<"-"; counter++; } auto first_counter = counter; cout << endl; // got a lock! - wait for 1s, then this thread prints "*" this_thread::sleep_for(chrono::milliseconds(1000)); cout <<counter - first_counter<< "*\n"; mtx.unlock(); } void main_fireworks() { thread threads[10]; for (int i = 0; i<10; ++i) threads[i] = thread(fireworks); for (auto& th : threads) th.join(); } timed_mutex cinderella; // gets time_point for next midnight: chrono::time_point<std::chrono::system_clock> midnight() { using chrono::system_clock; time_t tt = system_clock::to_time_t(system_clock::now()); struct tm *ptm = localtime(&tt); ++ptm->tm_mday; ptm->tm_hour = 0; ptm->tm_min = 0; ptm->tm_sec = 0; return system_clock::from_time_t(mktime(ptm)); } void carriage() { if (cinderella.try_lock_until(midnight())) { cout << "ride back home on carriage\n"; cinderella.unlock(); } else cout << "carriage reverts to pumpkin\n"; } void ball() { cinderella.lock(); cout << "at the ball...\n"; cinderella.unlock(); } void main_try_lock_until() { thread th1(ball); thread th2(carriage); th1.join(); th2.join(); } void main() { main_fireworks(); cout << endl; main_try_lock_until(); }执行结果:
------------------18*----------------16*
--------------14*
------------12*
----------10*
--------8*
------6*
----4*
--2*
0*
at the ball...
ride back home on carriage
请按任意键继续. . .这两端代码说明了,try_lock_for()在给定时间内只尝试执行1次的特性,和try_lock_until()在给定时间点内,执行起来和try_lock()一样。
---------------------------------------------------------------------------
recursive_timed_mutex类
Recursive timed mutex
A recursive timed mutex combines both the features of recursive_mutex and the features of timed_mutex into a single class: it supports both acquiring multiple lock levels by a single thread and also timed try-lock requests.
It is guaranteed to be a standard-layout class.
综合recursive_mutex 和 timed_mutex 和 mutex 的特性,可以在给定时间范围内上递归锁,可以在给定时间点内上递归锁。
---------------------------------------------------------------------------
lock_guard类
Lock guard
A lock guard is an object that manages a mutex object by keeping it always locked.
On construction, the mutex object is locked by the calling thread, and on destruction, the mutex is unlocked. It is the simplest lock, and is specially useful as an object with automatic duration that lasts until the end of its context. In this way, it guarantees the mutex object is properly unlocked in case an exception is thrown.
Note though that the lock_guard object does not manage the lifetime of the mutex object in any way: the duration of the mutex object shall extend at least until the destruction of the lock_guard that locks it.
lock_guard 比喻成一个 lock 的守卫,负责自动给语句区域内的 mtx 对象上锁解锁。
下面代码说明了调用 lock_guard<mutex> lck(mtx) 后,守卫会自动上锁,解锁。
代码如下:
mutex mtx; void print_even(int x) { if (x % 2 == 0) cout << x << " is even\n"; else throw (logic_error("not even")); } void print_thread_id(int id) { try { lock_guard<mutex> lck(mtx); print_even(id); } catch (logic_error&) { cout << "[exception caught]\n"; } } void main() { thread threads[10]; for (int i = 0; i<10; ++i) threads[i] = thread(print_thread_id, i + 1); for (auto& th : threads) th.join(); }结果如下:
[exception caught]
2 is even
[exception caught]
4 is even
[exception caught]
6 is even
[exception caught]
8 is even
[exception caught]
10 is even
请按任意键继续. . .
---------------------------------------------------------------------------
unique_lock类
Unique lock
A unique lock is an object that manages a mutex object with unique ownership in both states: locked and unlocked.
On construction (or by move-assigning to it), the object acquires a mutex object, for whose locking and unlocking operations becomes responsible.
The object supports both states: locked and unlocked.
This class guarantees an unlocked status on destruction (even if not called explicitly). Therefore it is especially useful as an object with automatic duration, as it guarantees the mutex object is properly unlocked in case an exception is thrown.
Note though, that the unique_lock object does not manage the lifetime of the mutex object in any way: the duration of the mutex object shall extend at least until the destruction of the unique_lock that manages it.
unique_lock 有和 lock_guard 一样的功能外,(lock_guard在离开作用域时unlock它guard的mutex)。unique还提供unlock函数,使用者可以手动执行unlock。此外,unique_lock还可以设置超时。
---------------------------------------------------------------------------
Functions
lock 锁互斥变量。
try_lock 尝试锁互斥变量。
call_once 用于保证某个函数不被多个线程同时执行。
用于保证某个函数不被多个线程同时执行,传入的 call_once(winner_flag, set_winner, id); 三个参数为 标志,导入函数,导入函数的参数
int winner; void set_winner(int x) { winner = x; } once_flag winner_flag; void wait_1000ms(int id) { // count to 1000, waiting 1ms between increments: for (int i = 0; i<1000; ++i) this_thread::sleep_for(chrono::milliseconds(1)); // claim to be the winner (only the first such call is executed): call_once(winner_flag, set_winner, id); } int main() { thread threads[10]; // spawn 10 threads: for (int i = 0; i<10; ++i) threads[i] = thread(wait_1000ms, i + 1); cout << "waiting for the first among 10 threads to count 1000 ms...\n"; for (auto& th : threads) th.join(); cout << "winner thread: " << winner << '\n'; return 0; }代码结果:
waiting for the first among 10 threads to count 1000 ms...
winner thread: 3
请按任意键继续. . .
浙公网安备 33010602011771号