C++ 11 多线程编程

  • 1、线程的创建和使用
    • 1.1 创建线程
    • 1.2 线程的使用
  • 2.实现线程同步
    • 2.1互斥锁
    • 2.2 条件变量
  • 3.例
    • 1:两线程交替打印奇偶数

1、线程的创建和使用

引入线程库<thread>,其提供了thread类,创建一个线程即实例化一个该类的对象,以此进行线程的创建和使用。

#include<thread>
using namespce std;

1.1 创建线程

一个线程可以用一个 thread 类的对象来表示,thread类中重载了构造函数和移动拷贝构造

#include<iostream>
#include<thread>using namespace std;void funa()
{cout << "thread_funa()" << endl;
}
int main()
{//创建线程——构造函数thread a(funa);cout << a.get_id() << endl;//移动拷贝构造thread b(std::move(a));cout << b.get_id() << endl;b.join();//移动拷贝构造进行资源的交换,所有要注释掉//a.join();cout << "main end" << endl;}

注意: thread类无拷贝构造函数、无赋值运算符,即不能直接用一个事先定义好的thread对象拷贝构造另一个thread对象,也不能不能进行赋值操作。但可以将临时的thread对象赋值给另一个thread即移动拷贝构造,也可以移动赋值。

1.2 线程的使用

thread 提供的函数: 线程支持库
thread类常用的成员函数:

成员函数 含义
get_id() 获取当前thread对象的线程id
joinable() 判断当前线程是否活跃(是:true;否:false)即是否支持调用 join()
join() 阻塞当前thread 对象所在的线程,直至thread 对象表示的线程执行完毕
detach() 将当前线程从调用该函数的线程中分离出去,他们彼此独立运行
swap() 交换两个线程的状态

注意:每个thread 对象在调用析构销毁前,要么调用 join() 函数令主线程等待子线程执行完毕,要么调用detach() 函数将子线程和主线程分离,二者必选一,否则存在以下问题:

  • 线程占用的资源无法全部释放,造成内存泄露
  • 当主线程执行完毕而子线程未完时,程序执行引发异常。

在this_thread 命名空间的部分函数:

函数 含义
get_id() 获得当前线程的 ID
yield() 阻塞当前线程让出cpu,直至条件成熟
sleep_for() 阻塞当前线程指定的时间
sleep_until() 阻塞当前线程,直至某个时间点为止

例:

//休眠1000毫秒,1s
this_thread::sleep_for(chrono::milliseconds(1000));

2.实现线程同步

2.1互斥锁

互斥锁用 mutex 类(位于std命名空间中)的对象表示,定义在头文件<mutex>头文件中。
成员函数:lock()——加锁; unlock()——解锁
例:对临界资源变量n的访问

#include<mutex>
using namespcase std;
mutex m; //实例化mutex对象m
void Show_n()
{m.lock();cout << n << endl;m.unlock();
}

实现严格基于作用域的互斥体所有权包装器:lock_fuard
原理:创建 lock_guard 对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥。即,创建即加锁,作用域结束自动解锁。

#include<mutex>
using namespcase std;
mutex m; //实例化mutex对象m
void Show_n()
{lock_guard<mutex> g1(m);cout << n << endl;
}
//作用域结束,析构g1,m解锁

lock_gurad传入两个参数,第二个为adopt_lock标识时,表示该互斥量之前必须已经lock,才能使用此参数,故需要提前手动锁定。

#include<mutex>
using namespcase std;
mutex m; //实例化mutex对象m
void Show_n()
{m.lock(); //手动锁定lock_guard<mutex> g1(m, adopt_lock);cout << n << endl;
}
//作用域结束,析构g1,m解锁

实现可移动的互斥体所有权包装器:unique_lock
与lock_guard类似,用法更丰富。

成员函数 含义
lock() 锁定
try_lock() 尝试锁定,若不能,先去执行其他代码并返回false;可以,进行加锁并返回true
unlock() 解锁

unique_lock与lock_guard

unique_lock lock_guard
手动lock、unlock 支持 不支持
参数 支持 adopt_lock/try_to_lock/defer_lock 支持 adopt_lock

try_to_lock: 尝试用mutx的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,不会阻塞在那里
defer_lock: 这个参数表示暂时先不lock,之后手动去lock,但是使用之前也是不允许去lock。

2.2 条件变量

C11提供的两种表示条件变量的类:
都定义在<condition_variable>头文件中,常与互斥锁搭配使用,避免线程间抢夺资源。

  • condition_variable
    该类表示的条件变量只能和unique_lock类表示的互斥锁搭配使用;
  • condition_variable_any
    该类表示的条件变量可以与任意类型的互斥锁搭配使用(如:递归互斥锁、定时互斥锁等)

条件变量常用的成员函数

成员函数 功能
wait() 阻塞当前线程,等待条件成立
wait_for() 阻塞当前线程的过程中,该函数会自动调用unlock()解锁,令其他线程使用公共资源。当条件成立或超过指定的等待时间,该函数自动调用lock()加锁,同时令线程继续执行
wait_until() 与wait_for() 类似,区别是其可以设定一个具体的时间点,当条件成立或等待时间超过了指定的时间点,函数自动加锁,线程执行
notify_one() 唤醒一个等待的线程
notify_all() 唤醒所有等待线程

关于wait() :
调用wait()1先获得mutex,2线程被阻塞,当wait陷入休眠是会自动释放mutex。直到另外某个线程调用 notify_one或notify_all唤醒了当前线程。当线程被唤醒时,此时线程是已经自动占有了mutex。

3.例

1:两线程交替打印奇偶数

#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
std::mutex data_mutex;
std::condition_variable data_var;
bool tag = true;
// odd : 1
// even: 2
// odd : 3
// even : 4void printodd() // 打印奇数
{std::unique_lock<std::mutex> ulock(data_mutex);for (int odd = 1; odd <= 100; odd += 2){while (!tag){data_var.wait(ulock);}cout << "odd: " << odd << endl;tag = false;data_var.notify_all();}
}
void printeven() //打印偶数
{std::unique_lock<std::mutex> ulock(data_mutex);for (int even = 2; even <= 100; even += 2){    while (tag){data_var.wait(ulock);//  1 2 }cout << "even: " << even << endl;tag = true;data_var.notify_all();}
}int main()
{thread thodd(printodd);thread theven(printeven);thodd.join();theven.join();return 0;
}

参考文献:
C++多线程unique_lock详解

C11 多线程初学1相关推荐

  1. c语言多线程的使用场合,C语言多线程,C11多线程完全攻略

    C 程序中一直同时执行多项任务.例如c 多线程控制控件实例,一个程序也许: (1) 在执行程序过程中借助完成并行任务来提升性能. (2) 在处理用户输入的同时,在后台进行耗时的数据通信和即时操作. 通 ...

  2. 【最新版】Java速成路线(急于找工作!)

    文章目录 计算机网络 分层结构 TCP/UDP HTTP/HTTPS 状态码 Cookie 和 Session URI和URL 操作系统 线程和进程 数据结构和算法 数据结构 算法 设计模式(23种) ...

  3. clang mingw

    下载在线安装软件https://sourceforge.net/projects/mingw-w64/files/mingw-w64/ 1.Version制定版本号,从4.9.1-8.1.0,按需选择 ...

  4. Java小白到大神的心路历程(Java SE)

    万事万物皆有其发展规律,学习Java也不例外. 以下是我根据教学经验,总结出来的小白在学习Java各阶段时的心路历程,当你在学习期间欣喜.困惑时,不妨看一看,也许这些都是必经之路. 使用Java完成第 ...

  5. 多线程java_初学Java要注意什么 怎么掌握Java多线程知识

    初学Java要注意什么?怎么掌握Java多线程知识?很多初学Java的同学通常会对进程和线程傻傻分不清,进程和线程都是由操作系统所体会的程序运行的基本单元,一个程序至少有一个进程,一个进程至少有一个线 ...

  6. 初学Java多线程:线程简介

     Java多线程初学者指南系列教程http://developer.51cto.com/art/200911/162925.htm 初学Java多线程:线程简介 2009-06-29 17:49 ...

  7. 初学Java多线程【1】:线程简介

    一.线程概述 线程是程序运行的基本执行单元.当操作系统(不包括单线程的操作系统,如微软早期的DOS)在执行一个程序时,会在系统中建立一个进程,而在这个进程中,必须至少建立一个线程(这个线程被称为主线程 ...

  8. 【java初学】匿名内部类和多线程

    文章目录 匿名内部类和多线程 1. 匿名内部类 1.1 接口你还记得吗??? 1.2 类的本体 2. 多线程 2.1 什么是进程 2.2 什么是线程 2.3 线程和进程的关系 2.4 线程优缺点 2. ...

  9. java 创建线程thread_初学Java多线程:用Thread类创建线程

    在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程, ...

  10. 初学多线程使用中踩过的坑

    wait使用,外面没有加synchronized ,导致频繁报错. IllegalMonitorStateException - 如果当前线程不是对象监视器的所有者报异常. wait使用时会释放锁.并 ...

最新文章

  1. 【Microsoft Word】编辑文字后,图片位置混乱的解决方法
  2. hdu 2544 最短路 Dijkstra算法
  3. VTK:图像透明度用法实战
  4. 获取Java接口的所有实现类
  5. jQuery Validate 合法性,限制性校验
  6. 只安装oracle服务端连plsql,Oracle 不安装Oracle客户端,使用PLSQL连接Oracle服务器
  7. 电子工程 计算机科学,计算机科学、计算机工程和电子工程有啥区别
  8. 【细节实现题】LeetCode 57. Insert Interval
  9. 配置元件--HTTP授权管理器
  10. php ajax可编辑表格,jquerAjax+php实现表格的增删改查(带数据库)
  11. jep java_jep-java-3.4-trial java表达式分析器 - 下载 - 搜珍网
  12. vi编辑器常用命令大全
  13. vlan tenant network on ovn based dvr (by quqi99)
  14. Javascript图片裁切
  15. 2020 web前端面试题及答案大全
  16. [转]使用jenkins实现持续集成
  17. 2020鼠年正月十一3000+股跌停后再次补仓
  18. 盛世昊通联手民族品牌CaldiceKris,创造价值互联
  19. Win7设置定时自动执行程序或脚本
  20. 计算机学院毕业礼物,“风里雨里,我们在这里等你“计算机学院举行毕业礼物赠予仪式...

热门文章

  1. 初级程序员考试大纲 (转)
  2. 逻辑思维训练500题(修订版)
  3. 秩和比(RSR)指标计算
  4. WAV声音档转PCM
  5. 利用vbs脚本编写Windows XP/2003序列号更改器
  6. 基于java的电子政务管理系统设计(含源文件)
  7. 超强整理-计量面板数据分析资料大全
  8. 金蝶K3WISE无窗口登录
  9. 利用quietHDD解决硬盘C1增长以及异响问题
  10. java写万年历_Java 实现万年历总结