2、
mutex 头文件中主要包含 Mutexes、lock 和相关的类型(Other types)和公共函数。
-
-
(constructor) 不允许拷贝;互斥对象不能被复制/移动,初始状态为未锁定。
default (1) constexpr mutex() noexcept;
copy [deleted] (2) mutex (const mutex&) = delete; -
- 如果互斥对象当前没有被任何线程锁定,则调用线程锁定它(从这一点开始,直到它的成员解锁被调用,线程拥有互斥对象)。
- 如果互斥锁当前被另一个线程锁定,则调用线程的执行将被阻塞,直到其他线程解锁(其他非锁定线程继续执行它们)。
- 如果互斥锁当前被相同的线程所锁定,调用此函数,则会产生死锁(未定义的行为)。
// mutex::lock/unlock #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex std::mutex mtx; // mutex for critical section void print_thread_id (int id) { // critical section (exclusive access to std::cout signaled by locking mtx): mtx.lock(); std::cout << "thread #" << id << '\n'; mtx.unlock(); } 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; }
-
// mutex::try_lock 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 countervoid 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];
// spawn 10 threads:
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;
}
``` -
-
-
传入时间段,在时间范围内未获得所就阻塞住线程,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时,则返回 false。 -
同上面的解释,只是传入参数为一个未来的一个时间点。
// timed_mutex::try_lock_for example #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(2))) { std::cout << "-"; } // got a lock! - wait for 1s, then this thread prints "*" std::this_thread::sleep_for(std::chrono::milliseconds(100)); 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; }
// timed_mutex::try_lock_until example #include <iostream> // std::cout #include <chrono> // std::chrono::system_clock #include <thread> // std::thread #include <mutex> // std::timed_mutex #include <ctime> // std::time_t, std::tm, std::localtime, std::mktime std::timed_mutex cinderella; // gets time_point for next midnight: std::chrono::time_point<std::chrono::system_clock> midnight() { using std::chrono::system_clock; std::time_t tt = system_clock::to_time_t (system_clock::now()); struct std::tm * ptm = std::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())) { std::cout << "ride back home on carriage\n"; cinderella.unlock(); } else std::cout << "carriage reverts to pumpkin\n"; } void ball() { cinderella.lock(); std::cout << "at the ball...\n"; cinderella.unlock(); } int main () { std::thread th1 (ball); std::thread th2 (carriage); th1.join(); th2.join(); return 0; }
-
-
定义: template <class Mutex> class lock_guard;
锁保护是一个通过将互斥对象保持锁定来管理互斥对象的对象。
在构造上,互斥对象被调用线程锁定,并且在销毁时,互斥锁被解锁。它是最简单的锁,作为具有自动持续时间的对象特别有用,直到它的上下文结束。这样,它保证在抛出异常时,可以正确地锁定互斥对象。
注意,lock_guard对象不以任何方式管理互斥对象的生命周期:互斥对象的持续时间至少要延长到锁定它的lock_guard的销毁为止。
模板参数 Mutex 代表互斥量类型,例如 std::mutex 类型,它应该是一个基本的 BasicLockable 类型,标准库中定义几种基本的 BasicLockable 类型,分别 std::mutex, std::recursive_mutex, std::timed_mutexstd::recursive_timed_mutex 以及 std::unique_lock。 (注:BasicLockable 类型的对象只需满足两种操作,lock 和 unlock,另外还有 Lockable 类型,在 BasicLockable 类型的基础上新增了 try_lock 操作,因此一个满足 Lockable 的对象应支持三种操作:lock,unlock 和 try_lock;最后还有一种 TimedLockable 对象,在 Lockable 类型的基础上又新增了 try_lock_for 和 try_lock_until 两种操作,因此一个满足 TimedLockable 的对象应支持五种操作:lock, unlock, try_lock, try_lock_for, try_lock_until)。
// constructing lock_guard with adopt_lock
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::lock_guard, std::adopt_lock
std::mutex mtx; // mutex for critical section
void print_thread_id (int id) {
mtx.lock();
std::lock_guard<std::mutex> lck (mtx, std::adopt_lock);
std::cout << "thread #" << id << '\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;
}
-
定义: template <class Mutex> class unique_lock;
unique_lock与 lock_guard 类相似,但它提供了更好的上锁和解锁控制。unique_lock 对象以独占所有权的方式(官方叫做unique ownership)管理 mutex 对象的上锁和解锁操作;独占所有权,就是没有其他的 unique_lock 对象同时拥有某个 mutex 对象的所有权。
unique_lock对象在析构的时候一定保证互斥量为解锁状态;因此它作为具有自动持续时间的对象特别有用,因为它保证在抛出异常时,互斥对象被正确地解锁。不过,注意unique_lock对象并不以任何方式管理互斥对象的生命周期:互斥对象的持续时间将延长至少直到unique_lock管理它的毁灭。
- 构造函数(constructor):
default (1) unique_lock() noexcept;
locking (2) explicit unique_lock (mutex_type& m);
try-locking (3) unique_lock (mutex_type& m, try_to_lock_t tag);
deferred (4) unique_lock (mutex_type& m, defer_lock_t tag) noexcept;
adopting (5) unique_lock (mutex_type& m, adopt_lock_t tag);
locking for (6) template <class Rep, class Period>
unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);
locking until (7) template <class Clock, class Duration>
unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);
copy [deleted] (8) unique_lock (const unique_lock&) = delete;
move (9) unique_lock (unique_lock&& x);
下面我们来分别介绍以上各个构造函数:
(1) 默认构造函数
unique_lock 对象不管理任何 Mutex 对象m。
(2) locking 初始化
unique_lock 对象管理 Mutex 对象 m,并调用 m.lock() 对 Mutex 对象进行上锁,如果其他 unique_lock 对象已经管理了m,该线程将会被阻塞。
(3) try-locking 初始化
unique_lock 对象管理 Mutex 对象 m,并调用 m.try_lock() 对 Mutex 对象进行上锁,但如果上锁不成功,不会阻塞当前线程。
(4) deferred 初始化
unique_lock 对象管理 Mutex 对象 m并不锁住m。 m 是一个没有被当前线程锁住的 Mutex 对象。
(5) adopting 初始化
unique_lock 对象管理 Mutex 对象 m, m 应该是一个已经被当前线程锁住的 Mutex 对象。(当前unique_lock 对象拥有对锁(lock)的所有权)。
(6) locking 一段时间(duration)
新创建的 unique_lock 对象管理 Mutex 对象 m,通过调用 m.try_lock_for(rel_time) 来锁住 Mutex 对象一段时间(rel_time)。
(7) locking 直到某个时间点(time point)
新创建的 unique_lock 对象管理 Mutex 对象m,通过调用 m.try_lock_until(abs_time) 来在某个时间点(abs_time)之前锁住 Mutex 对象。
(8) 拷贝构造 [被禁用]
unique_lock 对象不能被拷贝构造。
(9) 移动(move)构造
新创建的 unique_lock 对象获得了由 x 所管理的 Mutex 对象的所有权(包括当前 Mutex 的状态)。调用 move 构造之后, x 对象如同通过默认构造函数所创建的,就不再管理任何 Mutex 对象了。
:
// unique_lock constructor example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::lock, std::unique_lock
// std::adopt_lock, std::defer_lock
std::mutex foo,bar;
void task_a () {
std::lock (foo,bar); // simultaneous lock (prevents deadlock)
std::unique_lock<std::mutex> lck1 (foo,std::adopt_lock);
std::unique_lock<std::mutex> lck2 (bar,std::adopt_lock);
std::cout << "task a\n";
// (unlocked automatically on destruction of lck1 and lck2)
}
void task_b () {
// foo.lock(); bar.lock(); // replaced by:
std::unique_lock<std::mutex> lck1, lck2;
lck1 = std::unique_lock<std::mutex>(bar,std::defer_lock);
lck2 = std::unique_lock<std::mutex>(foo,std::defer_lock);
std::lock (lck1,lck2); // simultaneous lock (prevents deadlock)
std::cout << "task b\n";
// (unlocked automatically on destruction of lck1 and lck2)
}
int main ()
{
std::thread th1 (task_a);
std::thread th2 (task_b);
th1.join();
th2.join();
return 0;
}
- 其他成员函数
// unique_lock::lock/unlock
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock, std::defer_lock
std::mutex mtx; // mutex for critical section
void print_thread_id (int id) {
std::unique_lock<std::mutex> lck (mtx,std::defer_lock);
// critical section (exclusive access to std::cout signaled by locking lck):
lck.lock();
std::cout << "thread #" << id << '\n';
lck.unlock();
}
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;
}
// unique_lock::try_lock example
#include <iostream> // std::cout
#include <vector> // std::vector
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock, std::defer_lock
std::mutex mtx; // mutex for critical section
void print_star () {
std::unique_lock<std::mutex> lck(mtx,std::defer_lock);
// print '*' if successfully locked, 'x' otherwise:
if (lck.try_lock())
std::cout << '*';
else
std::cout << 'x';
}
int main ()
{
std::vector<std::thread> threads;
for (int i=0; i<500; ++i)
threads.emplace_back(print_star);
for (auto& x: threads) x.join();
std::cout << "\n";
return 0;
}
(3) std::unique_lock::try_lock_for
template <class Rep, class Period>
bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);
// unique_lock::try_lock_for example
#include <iostream> // std::cout
#include <chrono> // std::chrono::milliseconds
#include <thread> // std::thread
#include <mutex> // std::timed_mutex, std::unique_lock, std::defer_lock
std::timed_mutex mtx;
void fireworks () {
std::unique_lock<std::timed_mutex> lck(mtx,std::defer_lock);
// waiting to get a lock: each thread prints "-" every 200ms:
while (!lck.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";
}
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;
}
(4)std::unique_lock::try_lock_until
同上面描述,传入一个时间点而不是时间段。示例:同上改动,不再述。
// unique_lock::lock/unlock
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock, std::defer_lock
std::mutex mtx; // mutex for critical section
void print_thread_id (int id) {
std::unique_lock<std::mutex> lck (mtx,std::defer_lock);
// critical section (exclusive access to std::cout signaled by locking lck):
lck.lock();
std::cout << "thread #" << id << '\n';
lck.unlock();
}
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;
}
(6) std::unique_lock::operator=
move (1) unique_lock& operator= (unique_lock&& x) noexcept;
copy [deleted] (2) unique_lock& operator= (const unique_lock&) = delete;
// unique_lock::operator= example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
std::mutex mtx; // mutex for critical section
void print_fifty (char c) {
std::unique_lock<std::mutex> lck; // default-constructed
lck = std::unique_lock<std::mutex>(mtx); // move-assigned
for (int i=0; i<50; ++i) { std::cout << c; }
std::cout << '\n';
}
int main ()
{
std::thread th1 (print_fifty,'*');
std::thread th2 (print_fifty,'$');
th1.join();
th2.join();
return 0;
}
(7) std::unique_lock::swap
与x交换内容,包括托管的互斥对象和它们当前拥有的状态。
// unique_lock::release example
#include <iostream> // std::cout
#include <vector> // std::vector
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
std::mutex mtx;
int count = 0;
void print_count_and_unlock (std::mutex* p_mtx) {
std::cout << "count: " << count << '\n';
p_mtx->unlock();
}
void task() {
std::unique_lock<std::mutex> lck(mtx);
++count;
print_count_and_unlock(lck.release());
}
int main ()
{
std::vector<std::thread> threads;
for (int i=0; i<10; ++i)
threads.emplace_back(task);
for (auto& x: threads) x.join();
return 0;
}
// unique_lock::operator= example
#include <iostream> // std::cout
#include <vector> // std::vector
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock, std::try_to_lock
std::mutex mtx; // mutex for critical section
void print_star () {
std::unique_lock<std::mutex> lck(mtx,std::try_to_lock);
// print '*' if successfully locked, 'x' otherwise:
if (lck.owns_lock())
std::cout << '*';
else
std::cout << 'x';
}
int main ()
{
std::vector<std::thread> threads;
for (int i=0; i<500; ++i)
threads.emplace_back(print_star);
for (auto& x: threads) x.join();
std::cout << "\n";
return 0;
}
(10) std::unique_lock::operator bool
同上面。
// unique_lock::mutex example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock, std::defer_lock
class MyMutex : public std::mutex {
int _id;
public:
MyMutex (int id) : _id(id) {}
int id() {return _id;}
};
MyMutex mtx (101);
void print_ids (int id) {
std::unique_lock<MyMutex> lck (mtx);
std::cout << "thread #" << id << " locked mutex " << lck.mutex()->id() << '\n';
}
int main ()
{
std::thread threads[10];
// spawn 10 threads:
for (int i=0; i<10; ++i)
threads[i] = std::thread(print_ids,i+1);
for (auto& th : threads) th.join();
return 0;
}
-
struct once_flag {
constexpr once_flag() noexcept;
once_flag (const once_flag&) = delete;
once_flag& operator= (const once_flag&) = delete;
}; -
constexpr adopt_lock_t adopt_lock {};
定义adopt_lock 值用作对unique_lock或lock_guard的构造函数的可能参数。
使用adopt_lock构造unique_lock对象不锁定构建中的互斥对象,只是假设它已经被当前线程锁定了;这个值是一个没有状态的编译时常量,只是用来消除构造函数签名之间的歧义。
-
std::try_lock是一个模版函数:
template <class Mutex1, class Mutex2, class... Mutexes>
int try_lock (Mutex1& a, Mutex2& b, Mutexes&... cde);
使用try_lock成员函数(非阻塞)式的锁定对象a,b...等。函数为每个参数调用try_lock成员函数(首先是a,然后是b,最后是cde中的其他函数),直到所有调用都是成功的,或者只要调用失败(返回false或抛出异常)。如果函数结束是因为调用失败,则对所有调用try_lock成功的对象调用解锁,函数将返回锁定失败的对象的参数序号。没有对参数列表中的其余对象执行其他调用。
:
// std::lock example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::try_lock
std::mutex foo,bar;
void task_a () {
foo.lock();
std::cout << "task a\n";
bar.lock();
// ...
foo.unlock();
bar.unlock();
}
void task_b () {
int x = try_lock(bar,foo);
if (x==-1) {
std::cout << "task b\n";
// ...
bar.unlock();
foo.unlock();
}
else {
std::cout << "[task b failed: mutex " << (x?"foo":"bar") << " locked]\n";
}
}
int main ()
{
std::thread th1 (task_a);
std::thread th2 (task_b);
th1.join();
th2.join();
return 0;
}
-
std::lock同样为一个模版函数
template <class Mutex1, class Mutex2, class... Mutexes>
void lock (Mutex1& a, Mutex2& b, Mutexes&... cde);
锁定所有的参数互斥对象,阻塞当前调用线程;该函数使用一个未指定的调用序列来锁定对象,该序列调用其成员锁、try_lock和解锁,以确保所有参数都被锁定在返回(不产生任何死锁)。
如果函数不能锁定所有对象(例如,因为其中一个内部调用抛出异常),则函数首先解锁所有成功锁定的对象(如果有的话)。
:
// std::lock example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::lock
std::mutex foo,bar;
void task_a () {
// foo.lock(); bar.lock(); // replaced by:
std::lock (foo,bar);
std::cout << "task a\n";
foo.unlock();
bar.unlock();
}
void task_b () {
// bar.lock(); foo.lock(); // replaced by:
std::lock (bar,foo);
std::cout << "task b\n";
bar.unlock();
foo.unlock();
}
int main ()
{
std::thread th1 (task_a);
std::thread th2 (task_b);
th1.join();
th2.join();
return 0;
}
-
std::call_once 公有模版函数
template <class Fn, class... Args>
void call_once (once_flag& flag, Fn&& fn, Args&&... args);
call_once调用将args 作为fn的参数调用fn,除非另一个线程已经(或正在执行)使用相同的flag调用执行call_once。如果已经有一个线程使用相同flag调用call_once,会使得当前变为被动执行,所谓被动执行不执行fn也不返回直到恢复执行后返回。这这个时间点上所有的并发调用这个函数相同的flag都是同步的。
注意,一旦一个活跃调用返回了,所有当前被动执行和未来可能的调用call_once相同相同的flag也还不会成为积极执行。
:
// call_once example
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::sleep_for
#include <chrono> // std::chrono::milliseconds
#include <mutex> // std::call_once, std::once_flag
int winner;
void set_winner (int x) { winner = x; }
std::once_flag winner_flag;
void wait_1000ms (int id) {
// count to 1000, waiting 1ms between increments:
for (int i=0; i<1000; ++i)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
// claim to be the winner (only the first such call is executed):
std::call_once (winner_flag,set_winner,id);
}
int main ()
{
std::thread threads[10];
// spawn 10 threads:
for (int i=0; i<10; ++i)
threads[i] = std::thread(wait_1000ms,i+1);
std::cout << "waiting for the first among 10 threads to count 1000 ms...\n";
for (auto& th : threads) th.join();
std::cout << "winner thread: " << winner << '\n';
return 0;
}