boost互斥锁_c++并发编程之互斥锁(mutex)的使用方法

1. 多个线程访问同一资源时,为了保证数据的一致性,最简单的方式就是使用 mutex(互斥锁)。

引用 cppreference 的介绍:

The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.

方法1:直接操作 mutex,即直接调用 mutex 的 lock / unlock 函数

此例顺带使用了 boost::thread_group 来创建一组线程。

#include #include#includeboost::mutex mutex;int count = 0;voidCounter() {

mutex.lock();int i = ++count;

std::cout<< "count ==" << i <<:endl>

mutex.unlock();

}intmain() {//创建一组线程。

boost::thread_group threads;for (int i = 0; i < 4; ++i) {

threads.create_thread(&Counter);

}//等待所有线程结束。

threads.join_all();return 0;

}

方法2:使用 lock_guard 自动加锁、解锁。原理是 RAII,和智能指针类似

#include #include#include#includeboost::mutex mutex;int count = 0;voidCounter() {//lock_guard 在构造函数里加锁,在析构函数里解锁。

boost::lock_guard<:mutex> lock(mutex);int i = ++count;

std::cout<< "count ==" << i <<:endl>

}intmain() {

boost::thread_group threads;for (int i = 0; i < 4; ++i) {

threads.create_thread(&Counter);

}

threads.join_all();return 0;

}

方法3:使用 unique_lock 自动加锁、解锁

unique_lock 与 lock_guard 原理相同,但是提供了更多功能(比如可以结合条件变量使用)。

注意:mutex::scoped_lock 其实就是 unique_lock 的 typedef。

#include #include#includeboost::mutex mutex;int count = 0;voidCounter() {

boost::unique_lock<:mutex> lock(mutex);int i = ++count;

std::cout<< "count ==" << i <<:endl>

}intmain() {

boost::thread_group threads;for (int i = 0; i < 4; ++i) {

threads.create_thread(&Counter);

}

threads.join_all();return 0;

}

方法4:为输出流使用单独的 mutex

这么做是因为 IO 流并不是线程安全的!

如果不对 IO 进行同步,此例的输出很可能变成:

count == count == 2count == 41count== 3

因为在下面这条输出语句中:

std::cout << "count ==" << i << std::endl;

输出 "count == " 和 i 这两个动作不是原子性的(atomic),可能被其他线程打断。

#include #include#include#includeboost::mutex mutex;

boost::mutex io_mutex;int count = 0;voidCounter() {inti;

{

boost::unique_lock<:mutex> lock(mutex);

i= ++count;

}

{

boost::unique_lock<:mutex> lock(io_mutex);

std::cout<< "count ==" << i <<:endl>

}

}intmain() {

boost::thread_group threads;for (int i = 0; i < 4; ++i) {

threads.create_thread(&Counter);

}

threads.join_all();return 0;

}

2. 保护共享数据的替代设施

2.1 保护共享数据的初始化过程

丑陋的代码:

voidundefined_behaviour_with_double_checked_locking()

{if(!resource_ptr) //1

{

std::lock_guard<:mutex>lk(resource_mutex);if(!resource_ptr) //2

{

resource_ptr.reset(new some_resource); //3

}

}

resource_ptr->do_something(); //4

}

这个模式为什么声名狼藉呢?因为这里有潜在的条件竞争,因为外部的读取锁①没有与内部的

写入锁进行同步③。因此就会产生条件竞争,这个条件竞争不仅覆盖指针本身,还会影响到其

指向的对象;即使一个线程知道另一个线程完成对指针进行写入,它可能没有看到新创建的

some_resource实例,然后调用do_something()④后,得到不正确的结果。

C++标准库提供了 std::once_flag 和 std::call_once 来处理这种情况。比起锁住互斥量,并显式的检查指

针,每个线程只需要使用 std::call_once ,在 std::call_once 的结束时,就能安全的知道指

针已经被其他的线程初始化了。使用 std::call_once 比显式使用互斥量消耗的资源更少,特

别是当初始化完成后。

std::shared_ptrresource_ptr;

std::once_flag resource_flag;//1

voidinit_resource()

{

resource_ptr.reset(newsome_resource);

}voidfoo()

{

std::call_once(resource_flag,init_resource);//可以完整的进行一次初始化

resource_ptr->do_something();

}

2.2 保护很少更新的数据结构

虽然更新频度很低,但更新也是有可能发生的,并且当这个可缓存被多个线程访问,这个缓

存就需要适当的保护措施,来对其处于更新状态时进行保护,也为了确保线程读到缓存中的

有效数据。

使用一

个 std::mutex 来保护数据结构,这的确有些反应过度,因为在没有发生修改时,它将削减并

发读取数据的可能性;这里需要另一种不同的互斥量。这种新的互斥量常被称为“读者-写者

锁”(reader-writer mutex),因为其允许两中不同的使用方式:一个“作者”线程独占访问和共

享访问,让多个“读者”线程并发访问。

新的C++标准库应该不提供这样的互斥量,Boost库提供了boost::shared_mutex。

3.3 嵌套锁

C++标准库提供了 std::recursive_mutex 类。其功能与 std::mutex 类似,除了你可以从

同一线程的单个实例上获取多个锁。在互斥量锁住其他线程前,你必须释放你拥有的所有

锁,所以当你调用lock()三次时,你也必须调用unlock()三次。正确使

用 std::lock_guard<:recursive_mutex> 和 std::unique_lock<:recursice_mutex> 可以帮

你处理这些问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值