多线程能提高程序的效率,但同时也带来了相应的问题----数据竞争。当多个线程同时操作同一个变量时,就会出现数据竞争。出现数据竞争,一般会用临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)这四种方法来完成线程同步。

1、临界区

对于临界资源,多线程必须互斥地对它进行访问。每个线程访问临界资源的那段代码就称为临界区。它保证每次只能有一个线程进入临界区。有一个线程进入临界区后其他试图访问临界区的线程会被挂起。临界区被释放后,其他线程才可以继续抢占。几种同步处理中,临界区速度最快,但它只能实现同进程中的多个线程同步,无法实现多进程同步。c++11并没有为我们提供临界区类。

2、互斥量

互斥量与临界区相似,但临界区不支持多进程,而mutex支持多进程。c++11标准库中提供了mutex类。

[cpp] view plaincopy
  1. #include "stdafx.h"
  2. #include <vector>
  3. #include <thread>
  4. #include <iostream>
  5. #include <mutex>
  6. using namespace std;
  7. mutex sLock;
  8. int i = 0;
  9. void test()
  10. {
  11. for (int j = 0; j < 1000; j++)
  12. {
  13. sLock.lock();
  14. i++;
  15. sLock.unlock();
  16. }
  17. }
  18. int main(int argc, _TCHAR* argv[])
  19. {
  20. vector<thread> v;
  21. for (int j = 0; j < 100; j++)
  22. {
  23. v.emplace_back(test);
  24. }
  25. for(auto& t : v)
  26. {
  27. t.join();
  28. }
  29. cout << i << endl;
  30. }

曾有人对c++11中的thread和mutex性能进行了测试。点击打开链接根据他的测试结果,std::thread的性能损耗不大,但std::mutex的性能损耗非常大。所以如果设计中要考虑性能的话,应该避免使用c++11标准库中的mutex

3、信号量

信号量对象对线程的同步方式与前面几种方法不同,信号量允许多个线程同时使用共享资源。它的原理是:

P操作 申请资源: 
  (1)S减1; 
  (2)若S减1后仍大于等于零,则进程继续执行; 
  (3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转入进程调度。 
V操作 释放资源: 
  (1)S加1; 
  (2)若相加结果大于零,则进程继续执行; 
  (3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度。

4、事件

事件对象可以通过通知操作的方式来保持线程同步。

******************************************************************************************************************************************

什么是条件变量?

条件变量是一种同步机制,允许线程挂起,直到共享数据上的某些条件得到满足。条件变量上的基本操作有:触发条件(当条件变为 true 时);等待条件,挂起线程直到其他线程触发条件。条件变量要和互斥量相联结,以避免出现条件竞争--一个线程预备等待一个条件变量,当它在真正进入等待之前,另一个线程恰好触发了该条件。

什么意思呢?不清楚没关系。看了例子就知道了:问题描述:假设有一个bool型全局变量 isTrue ,现有10个线程,线程流程如下:当isTrue为真时,doSomething;否则挂起线程,直到条件满足。那么,用thread和mutex如何实现这个功能呢?

[cpp] view plaincopy
  1. #include <vector>
  2. #include <iostream>
  3. #include <thread>
  4. #include <mutex>
  5. #include <chrono>
  6. using namespace  std;
  7. bool isTrue = false;
  8. void doSomething()
  9. {
  10. cout << "this is : " << this_thread::get_id() << endl;
  11. }
  12. void thread_Func()
  13. {
  14. while (!isTrue)
  15. this_thread::yield();
  16. doSomething();
  17. }
  18. int main()
  19. {
  20. vector<thread> v;
  21. v.reserve(10);
  22. for (int i = 0; i < 10; i++)
  23. {
  24. v.emplace_back(thread_Func);
  25. }
  26. this_thread::sleep_for(chrono::seconds(2));
  27. isTrue = true;
  28. for (auto& t : v)
  29. {
  30. t.join();
  31. }
  32. return 1;
  33. }

这段代码虽然能满足需求,但有一个大缺点,就是当条件为假时,子线程会不停的测试条件,这样会消耗系统资源。我们的思想是,当条件为假时,子线程挂起,直到条件为真时,才唤醒子线程。
nice,条件变量就是干这事的!

先来看看条件变量的介绍:

条件变量能够挂起调用线程,直到接到通知才会唤醒线程。它使用unique_lock<Mutex>来配合完成。下面是用条件变量实现需求:

[cpp] view plaincopy
  1. #include <iostream>
  2. #include <vector>
  3. #include <thread>
  4. #include <mutex>
  5. #include <chrono>
  6. #include <condition_variable>
  7. using namespace  std;
  8. bool isTrue = false;
  9. std::mutex mtx;
  10. std::condition_variable cv;
  11. void doSomething()
  12. {
  13. cout << "this is : " << this_thread::get_id() << endl;
  14. }
  15. void thread_Func()
  16. {
  17. unique_lock<mutex> loc(mtx);
  18. while (!isTrue)
  19. cv.wait(loc);
  20. doSomething();
  21. }
  22. int main()
  23. {
  24. vector<thread> v;
  25. v.reserve(10);
  26. for (int i = 0; i < 10; i++)
  27. {
  28. v.emplace_back(thread_Func);
  29. }
  30. this_thread::sleep_for(chrono::seconds(2));
  31. {
  32. unique_lock<mutex> loc(mtx);
  33. isTrue = true;
  34. cv.notify_all();
  35. }
  36. for (auto& t : v)
  37. {
  38. t.join();
  39. }
  40. return 1;
  41. }

我们发现,在条件变量cv的wait函数中,我们传入了一个lock参数,为什么要用锁呢?因为isTrue为临界变量,主线程中会“写”它,子线程中要“读”它,这样就产生了数据竞争,并且这个竞争很危险。

我们把while (!isTrue) cv.wait(loc)拆开:

1、条件判断
2、挂起线程

假如现在1执行完后,时间片轮转,该子线程暂停执行。而恰好在这时,主线程修改了条件,并调用了cv.notify_all()函数。这种情况下,该子线程是收不到通知的,因为它还没挂起。等下一次调度子线程时,子线程接着执行2将自己挂起。但现在主线程中的notify早已经调用过了,不会再调第二次了,所以该子线程永远也无法唤醒了。

为了解决上面的情况,就要使用某种同步手段来给线程加锁。而c++11的condition_variable选择了用unique_lock<Mutex>来配合完成这个功能。并且我们只需要加锁,条件变量在挂起线程时,会调用原子操作来解锁。

c++11还为我们提供了一个更方便的接口,连同条件判断,一起放在条件变量里。上面的线程函数可做如下修改:

[cpp] view plaincopy
  1. void thread_Func()
  2. {
  3. unique_lock<mutex> loc(mtx);
  4. cv.wait(loc, []() -> bool { return isTrue;} );
  5. doSomething();
  6. }

C++11 多线程同步相关推荐

  1. [C++11 多线程同步] --- 线程同步概述

    1 线程调度的几个基本知识点 多线程并发执行时有很多同学捋不清楚调度的随机性会导致哪些问题,要知道如果访问临界资源不加锁会导致一些突发情况发生甚至死锁. 关于线程调度,需要深刻了解以下几个基础知识点: ...

  2. [C++11 多线程同步] --- 条件变量的那些坑【条件变量信号丢失和条件变量虚假唤醒(spurious wakeup)】

    1 条件变量的信号丢失 1.1 条件变量的信号丢失场景重现 拿生产者和消费者模型举例,看一段示例代码: #include <iostream> #include <vector> ...

  3. java提供对多线程同步语言级的支持_赞同科技笔试题11

    java面试题 一.不定项选择题(45分) 1. ()关于运算符>>和>>>描述正确的是 A.>>执行移动 B.>>执行翻转 C. >> ...

  4. python多线程读取文件的问题_Python多线程同步---文件读写控制方法

    1.实现文件读写的文件ltz_schedule_times.py #! /usr/bin/env python #coding=utf-8 import os def ReadTimes(): res ...

  5. 使用NSCondition实现多线程同步

    iOS中实现多线程技术有非常多方法. 这里说说使用NSCondition实现多线程同步的问题,也就是解决生产者消费者问题(如收发同步等等). 问题流程例如以下: 消费者取得锁,取产品,假设没有,则wa ...

  6. C++11 多线程库使用说明

    多线程基础 1.1 进程与线程 根本区别: 进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位 开销方面: 每个进程都有自己独立的代码和数据空间,程序之间的切换开销较大. 线程可以看作是 ...

  7. Linux与C++11多线程编程(学习笔记)

    多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...

  8. python 多线程同步_Python利用多线程同步锁实现多窗口订票系统(推荐)

    利用Python实现多窗口订票系统,利用 threading.Lock() 避免出现一票多卖,无票也卖的情况,并规范化输出情况. 代码: import threading import time ti ...

  9. java里的多线程同步机制

    2019独角兽企业重金招聘Python工程师标准>>> 要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性.多个线程之间是不能直接传递数据交互的,它们之间的交互只能通 ...

最新文章

  1. 【博客美化】公告栏显示个性时间
  2. dynamodb容器使用_使用DynamoDB映射器将DynamoDB项目映射到对象
  3. iOS项目开发— CoreLocation的定位服务和地理编码与发编码实现
  4. 进程通信:匿名管道和命名管道
  5. gdb tui 安装_GDB 单步调试汇编
  6. 关于VS2005安装项目制作的三个参考文章链接
  7. overleaf 插入visio图像
  8. IDEA离线安装插件
  9. C++练习 计算年份所属生肖
  10. 混合动力simulink模型 转卖新能源混动车型模式转换说明,包含HCU模式转换simulink框图及说明文档
  11. JS最佳实践——红皮书
  12. WebRTC的JitterBuffer笔记
  13. 高德开放平台天气查询API
  14. 22.Odoo产品分析 (三) – 人力资源板块(3) – 休假管理(1)
  15. java--五子棋人机大战--课程设计
  16. spreadjs导出学习总结
  17. 2019计算机调剂武汉大学,2019年武汉大学考研调剂相关规定
  18. MD5 c++ 实现
  19. (6)打鸡儿教你Vue.js
  20. 苹果电脑MAC地址怎么查询

热门文章

  1. Vue事件总线(EventBus)、$on、$emit、$off
  2. java在线考试管理系统整理
  3. pycharm使用技巧 之 新建文件自动添加表头信息
  4. 2019年CSP-J2第三题:纪念品(souvenir)题解
  5. 如何建立属于自己的云服务器
  6. 多进程(Multiprocessing)
  7. Php 上传视频到优酷,让织梦CMS的后台编辑器支持优酷视频
  8. 使用Nunjucks
  9. 给刚参加工作的大学生十点建议
  10. 别扯了,这世界上根本没有稳定的工作