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 <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp> boost::mutex mutex;
int count = ; void Counter() {
mutex.lock(); int i = ++count;
std::cout << "count == " << i << std::endl; // 前面代码如有异常,unlock 就调不到了。
mutex.unlock();
} int main() {
// 创建一组线程。
boost::thread_group threads;
for (int i = ; i < ; ++i) {
threads.create_thread(&Counter);
} // 等待所有线程结束。
threads.join_all();
return ;
}

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

#include <iostream>
#include <boost/thread/lock_guard.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp> boost::mutex mutex;
int count = ; void Counter() {
// lock_guard 在构造函数里加锁,在析构函数里解锁。
boost::lock_guard<boost::mutex> lock(mutex); int i = ++count;
std::cout << "count == " << i << std::endl;
} int main() {
boost::thread_group threads;
for (int i = ; i < ; ++i) {
threads.create_thread(&Counter);
} threads.join_all();
return ;
}

方法3:使用 unique_lock 自动加锁、解锁
unique_lock 与 lock_guard 原理相同,但是提供了更多功能(比如可以结合条件变量使用)。
注意:mutex::scoped_lock 其实就是 unique_lock<mutex> 的 typedef

#include <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp> boost::mutex mutex;
int count = ; void Counter() {
boost::unique_lock<boost::mutex> lock(mutex); int i = ++count;
std::cout << "count == " << i << std::endl;
} int main() {
boost::thread_group threads;
for (int i = ; i < ; ++i) {
threads.create_thread(&Counter);
} threads.join_all();
return ;
}

方法4:为输出流使用单独的 mutex
这么做是因为 IO 流并不是线程安全的!
如果不对 IO 进行同步,此例的输出很可能变成:

count == count == 2count ==
count ==

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

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

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

#include <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/lock_guard.hpp> boost::mutex mutex;
boost::mutex io_mutex;
int count = ; void Counter() {
int i;
{
boost::unique_lock<boost::mutex> lock(mutex);
i = ++count;
} {
boost::unique_lock<boost::mutex> lock(io_mutex);
std::cout << "count == " << i << std::endl;
}
} int main() {
boost::thread_group threads;
for (int i = ; i < ; ++i) {
threads.create_thread(&Counter);
} threads.join_all();
return ;
}

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

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

丑陋的代码:

void undefined_behaviour_with_double_checked_locking()
{
if(!resource_ptr) //
{
std::lock_guard<std::mutex> lk(resource_mutex);
if(!resource_ptr) //
{
  resource_ptr.reset(new some_resource); //
}
}
resource_ptr->do_something(); //
}

这个模式为什么声名狼藉呢?因为这里有潜在的条件竞争,因为外部的读取锁①没有与内部的
写入锁进行同步③。因此就会产生条件竞争,这个条件竞争不仅覆盖指针本身,还会影响到其
指向的对象;即使一个线程知道另一个线程完成对指针进行写入,它可能没有看到新创建的
some_resource实例,然后调用do_something()④后,得到不正确的结果。

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

针,每个线程只需要使用 std::call_once ,在 std::call_once 的结束时,就能安全的知道指
针已经被其他的线程初始化了。使用 std::call_once 比显式使用互斥量消耗的资源更少,特
别是当初始化完成后。

std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag; //
void init_resource()
{
resource_ptr.reset(new some_resource);
}
void foo()
{
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<std::recursive_mutex> 和 std::unique_lock<std::recursice_mutex> 可以帮
你处理这些问题。

c++并发编程之互斥锁(mutex)的使用方法的更多相关文章

  1. python 并发编程 多进程 互斥锁 目录

    python 并发编程 多进程 互斥锁 模拟抢票 互斥锁与join区别

  2. python 并发编程 多进程 互斥锁

    运行多进程  每个子进程的内存空间是互相隔离的 进程之间数据不能共享的 一 互斥锁 但是进程之间都是运行在一个操作系统上,进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终 ...

  3. C++ 并发编程之互斥锁和条件变量的性能比较

    介绍 本文以最简单生产者消费者模型,通过运行程序,观察该进程的cpu使用率,来对比使用互斥锁 和 互斥锁+条件变量的性能比较. 本例子的生产者消费者模型,1个生产者,5个消费者. 生产者线程往队列里放 ...

  4. python 并发编程 多线程 互斥锁

    互斥锁 并行变成串行,牺牲效率 保证数据安全,实现局部串行 保护不同的数据,应该加不同的锁 现在一个进程 可以有多个线程 所有线程都共享进程的地址空间 实现数据共享 共享带来问题就会出现竞争 竞争就会 ...

  5. python 并发编程 多进程 互斥锁与join区别

    互斥锁与join 互斥锁和join都可以把并发变成串行 以下代码是用join实现串行 from multiprocessing import Process import time import js ...

  6. 并发编程 Process 互斥锁

    进程理论 程序与进程的区别 ''' 程序不是存在硬盘上的代码,相对来说是静态的 进程表示程序在执行的过程,是动态的 ''' 进程的调度 先来先服务调度算法 '''对长作业有利,对短作业无益''' 短作 ...

  7. 互斥锁Mutex与信号量Semaphore的区别

    转自互斥锁Mutex与信号量Semaphore的区别 多线程编程中,常常会遇到这两个概念:Mutex和Semaphore,两者之间区别如下: 有人做过如下类比: Mutex是一把钥匙,一个人拿了就可进 ...

  8. Golang之并发资源竞争(互斥锁)

    并发本身并不复杂,但是因为有了资源竞争的问题,就使得我们开发出好的并发程序变得复杂起来,因为会引起很多莫名其妙的问题. package main import ( "fmt" &q ...

  9. 线程锁(互斥锁Mutex)

    线程锁(互斥锁Mutex) 一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况? # -*- cod ...

随机推荐

  1. 20155223 Exp2 后门原理与实践

    20155223 Exp2 后门原理与实践 实验预热 一.windows获取Linux shell Windows:使用 ipconfig 命令查看当前机器IP地址. 进入ncat所在文件地址,输入命 ...

  2. 20155333 《网络对抗》Exp4 恶意代码分析

    20155333 <网络对抗>Exp4 恶意代码分析 基础问题回答 1.如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪 ...

  3. Bluedroid协议栈HCI线程分析

    蓝牙进程中有多个线程,其中HCI 线程是负责处理蓝牙主机端和控制器的数据处理和收发的工作. 本篇文章就是分析一下该线程的数据处理流程. 1.跟HCI相关的接口 首先看看hci的相关的接口:在hci_l ...

  4. python删除文件与目录的方法

    python内置方法删除目录(空目录与非空目录)及文件 1.os.remove(file_path):删除文件 #PPTV是文件夹,xen.txt是文件 >>> os.remove( ...

  5. RabbitMQ基础教程之基本使用篇

    RabbitMQ基础教程之基本使用篇 最近因为工作原因使用到RabbitMQ,之前也接触过其他的mq消息中间件,从实际使用感觉来看,却不太一样,正好趁着周末,可以好好看一下RabbitMQ的相关知识点 ...

  6. vector 简介

    vector简介 vector是STL中最常见的容器,它是一种顺序容器,支持随机访问.vector是一块连续分配的内存,从数据安排的角度来讲,和数组极其相似, 不同的地方就是:数组是静态分配空间,一旦 ...

  7. 01-docker简介

    如今Docker的使用已经非常普遍,特别在一线互联网公司.使用Docker技术可以帮助企业快速水平扩展服务,从而到达弹性部署业务的能力.在云服务概念兴起之后,Docker的使用场景和范围进一步发展,如 ...

  8. Selenium--调用js,对话框处理 (python)

    前言: 本次教程针对Python语言,selenium教程(调用js,对话框处理) 一.对话框处理 更多的时候我们在实际的应用中碰到的并不是简单警告框,而是提供更多功能的会话框. 本节重点: 1.打开 ...

  9. centos运行C程序

    gcc -o Hello Hello.c 编译成可执行文件 ./Hello 运行 win上也是一样

  10. 在centOS中安装mongodb

    自己在一个CentOS6.6的系统中按照官网的说明,走了一遍的安装过程,记录一下. 看过个mongo的视频,上面介绍的安装是用源码安装,而官网上说需要gcc4.8.3的版本,还有scons的编译工具, ...