走进C++11(二十五)一统江湖之mutex -- std::mutex

图片

 

Mutex 又称互斥量,C++ 11中与 Mutex 相关的类(包括锁类型)和函数都声明在 <mutex> 头文件中,所以如果你需要使用 std::mutex,就必须包含 <mutex> 头文件。

C++11中新增了<mutex>,它是C++标准程序库中的一个头文件,定义了C++11标准中的一些互斥访问的类与方法等。

 

C++11标准库定义了4个互斥类:

 

图片

 

std::mutex

std::mutex 是C++11 中最基本的互斥量,std::mutex 对象提供了独占所有权的特性——即不支持递归地对 std::mutex 对象上锁,而 std::recursive_lock 则可以递归地对互斥量对象上锁。

 

  •  

下面给出一个与 std::mutex 的小例子(http://www.cplusplus.com/reference/mutex/mutex/try_lock/):

 

// mutex::try_lock example#include <iostream>       // std::cout#include <thread>         // std::thread#include <mutex>          // std::mutex
volatile int counter (0); // non-atomic counterstd::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];  // 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;}

std::recursive_mutex

官方文档是这么说的:

 

recursive_mutex 类是同步原语,能用于保护共享数据免受个多线程同时访问。

 

recursive_mutex 提供排他性递归所有权语义:

  • 调用方线程在从它成功调用 lock 或 try_lock 开始的时期里占有 recursive_mutex 。此时期间,线程可以进行对 lock 或 try_lock 的附加调用。所有权的时期在线程调用 unlock 匹配次数时结束。

  • 线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调用 lock )或收到 false 返回值(对于调用 try_lock )。

  • 可锁定 recursive_mutex 次数的最大值是未指定的,但抵达该数后,对 lock 的调用将抛出 std::system_error 而对 try_lock 的调用将返回 false 。

     

若 recursive_mutex 在仍为某线程占有时被销毁,则程序行为未定义。recursive_mutex 类满足互斥体 (Mutex) 和标准布局类型 (StandardLayoutType) 的所有要求。


用人话说就是:递归互斥锁可以被同一个线程多次加锁,以获得对互斥锁对象的多层所有权。例如,同一个线程多个函数访问临界区时都可以各自加锁,执行后各自解锁。std::recursive_mutex释放互斥量时需要调用与该锁层次深度相同次数的unlock(),即lock()次数和unlock()次数相同。可见,线程申请递归互斥锁时,如果该递归互斥锁已经被当前调用线程锁住,则不会产生死锁。

 

recursive_mutex 的使用场景之一是保护类中的共享状态,而类的成员函数可能相互调用。官方的小例子:

 

#include <iostream>#include <thread>#include <mutex> class X {    std::recursive_mutex m;    std::string shared;  public:    void fun1() {      std::lock_guard<std::recursive_mutex> lk(m);      shared = "fun1";      std::cout << "in fun1, shared variable is now " << shared << '\n';    }    void fun2() {      std::lock_guard<std::recursive_mutex> lk(m);      shared = "fun2";      std::cout << "in fun2, shared variable is now " << shared << '\n';      fun1(); // 递归锁在此处变得有用      std::cout << "back in fun2, shared variable is " << shared << '\n';    };}; int main() {    X x;    std::thread t1(&X::fun1, &x);    std::thread t2(&X::fun2, &x);    t1.join();    t2.join();}

 

可以看出fun2调用的fun1,他们都对recursive_mutex m 加锁,如果不是recursive的,那么这里会产生死锁。假设fun2拿到了锁,然后调用fun1又要去拿锁,又不是recursive的,那么就会hang在fun1里边。

 

std::time_mutex

官方文档是这么说的:

 

recursive_timed_mutex 是同步原语,能用于保护共享数据免受从多个线程同时访问。

 

以类似 std::recursive_mutex 的方式, recursive_timed_mutex 提供排他性递归所有权语义。另外, recursive_timed_mutex 通过 try_lock_for 与 try_lock_until 方法,提供带时限地试图要求 recursive_timed_mutex 所有权的能力。

 

recursive_timed_mutex 类满足定时互斥体 (TimedMutex) 和标准布局类型 (StandardLayoutType) 的所有要求。 

 

换成人话说:

 

std::time_mutex 比 std::mutex 多了两个成员函数,try_lock_for(),try_lock_until()。

 

try_lock_for 函数接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与 std::mutex 的 try_lock() 不同,try_lock 如果被调用时没有获得锁则直接返回 false),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。

 

try_lock_until 函数则接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。

 

举个try_lock_for的小例子:

 

#include <iostream>#include <mutex>#include <thread>#include <vector>#include <sstream> std::mutex cout_mutex; // 控制到 std::cout 的访问std::timed_mutex mutex; void job(int id) {    using Ms = std::chrono::milliseconds;    std::ostringstream stream;     for (int i = 0; i < 3; ++i) {        if (mutex.try_lock_for(Ms(100))) {            stream << "success ";            std::this_thread::sleep_for(Ms(100));            mutex.unlock();        } else {            stream << "failed ";        }        std::this_thread::sleep_for(Ms(100));    }     std::lock_guard<std::mutex> lock(cout_mutex);    std::cout << "[" << id << "] " << stream.str() << "\n";} int main() {    std::vector<std::thread> threads;    for (int i = 0; i < 4; ++i) {        threads.emplace_back(job, i);    }     for (auto& i: threads) {        i.join();    }}

 

可能的输出:

 

[0] failed failed failed [3] failed failed success [2] failed success failed [1] success failed success

 

std::recursive_timed_mutex

这个就好说的,就是叠加recursive 和time两大神功的mutex。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值