《C++(三)--多线程》
C++(三)–多线程
线程是进程的组成单位。不管在windows或者linux操作系统上,进程都是操作系统资源申请、调度和独立运行的基本单位;一个程序可以有多个进程,例如可以同时打开多个程序的进程。另外,真正执行代码的是线程,进程只是线程的容器(执行环境)。
多线程的实现能够提高执行效率。
参考:《VC++深入详解》----孙鑫
目录
- 一、 多线程的概念
- 1.1 线程
- 1.2 单线程和多线程
- 二、C++ 11 多线程的实现
- 2.1 thread类的定义
- 2.2 多线程简单的实例
- 2.3 互斥锁
- 2.3.1 使用std::mutex
- 2.3.2 使用std::lock_guard()
- 2.3.3 使用std::unique_lock()
- 2.4 原子操作
一、 多线程的概念
1.1 线程
线程主要由两个部分组成,一个是线程内核对象,操作系统用它来实施线程管理;另一个是线程栈,用于维护线程在执行代码时需要的所有函数参数和局部变量。线程会在进程环境中创建,系统从进程的地址空间中分配内存,供线程的使用。
另外线程只有一个内核对象和一个栈,需要的内存很少,开销也比进程少;所以在编程中,往往用多线程来解决问题,尽量避免创建新的进程。
1.2 单线程和多线程
对于单线程来说,进程中只有一个线程在运行。比如一个病人需要动手术,医院安排了一位医生为他动手术,这个医生就是主线程,仅仅由这个主线程完成任务;对于多线程而言,进程中有多个线程,其中有一个是主线程,就好比在上述例子中医生多了几个护士,由医生和几个护士共同完成手术过程,那么手术的效率就高了。
二、C++ 11 多线程的实现
要基于C++ 11 实现多线程,需要包含头文件 < thread >。
2.1 thread类的定义
class thread{ // class for observing and managing threads
public:class id;typedef void *native_handle_type;thread() _NOEXCEPT{ // construct with no thread_Thr_set_null(_Thr);}template<class _Fn,class... _Args,class = typename enable_if<!is_same<typename decay<_Fn>::type, thread>::value>::type>explicit thread(_Fn&& _Fx, _Args&&... _Ax){ // construct with _Fx(_Ax...)_Launch(&_Thr,_STD make_unique<tuple<decay_t<_Fn>, decay_t<_Args>...> >(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...));}~thread() _NOEXCEPT{ // clean upif (joinable())_XSTD terminate();}thread(thread&& _Other) _NOEXCEPT: _Thr(_Other._Thr){ // move from _Other_Thr_set_null(_Other._Thr);}thread& operator=(thread&& _Other) _NOEXCEPT{ // move from _Otherreturn (_Move_thread(_Other));}thread(const thread&) = delete;thread& operator=(const thread&) = delete;void swap(thread& _Other) _NOEXCEPT{ // swap with _Other_STD swap(_Thr, _Other._Thr);}bool joinable() const _NOEXCEPT{ // return true if this thread can be joinedreturn (!_Thr_is_null(_Thr));}void join();void detach(){ // detach threadif (!joinable())_Throw_Cpp_error(_INVALID_ARGUMENT);_Thrd_detachX(_Thr);_Thr_set_null(_Thr);}id get_id() const _NOEXCEPT;static unsigned int hardware_concurrency() _NOEXCEPT{ // return number of hardware thread contextsstatic const unsigned int _Cnt = _Thrd_hardware_concurrency();return (_Cnt);}native_handle_type native_handle(){ // return Win32 HANDLE as void *return (_Thr._Hnd);}private:thread& _Move_thread(thread& _Other){ // move from _Otherif (joinable())_XSTD terminate();_Thr = _Other._Thr;_Thr_set_null(_Other._Thr);return (*this);}_Thrd_t _Thr;};
其中,thread类中的方法有:
void swap(thread& _Other);//bool joinable() ;//是否可以join调用void join(); //阻塞主线程等待子线程结束void detach();//分离子线程,主线程不被阻塞继续执行id get_id() ;//获取线程id
2.2 多线程简单的实例
创建线程的方法为std::thread th(函数名,参数1,参数2,...,参数n)
,若声明一个函数function(int a,int b)
,则创建线程的方法为std::thread th(fuction,a,b)
,
#include <iostream>
#include <thread>using namespace std;
void fun1(int num)
{cout<<"fun 1:"<<num<<endl;cout<<"fun1 线程id:"<<this_thread::get_id()<<endl;
}void fun2(int num)
{cout<<"fun 2:"<<num<<endl;cout<<"fun2 线程id:"<<this_thread::get_id()<<endl;
}int main()
{cout <<"主线程id:"<<this_thread::get_id()<< endl;std::thread a(fun1,1);std::thread b(fun2,2);a.join();b.join();return 0;
}
执行结果:
另外需要注意的是,当线程定义申明的时候就开始运行。join
函数是等待子线程阻塞主线程,而detach
函数是分离线程,主线程不会阻塞,继续执行。
2.3 互斥锁
C++ 11中头文件< mutex >中,定义了两个常用的类std::mutex
和std::lock_guard
,要使用互斥锁时,需要首先创建对象std::mutex mutex_
;如果你用过Qt的互斥锁,会发现他们是一样的,Qt中使用QMutex来实现互斥锁。这里介绍的是C++ 11 实现互斥。
在这里,如果需要引用传递参数,需要使用std::ref()
函数,如下面所示:
#include <iostream>
#include <thread>
#include <mutex>using namespace std;
mutex mutex_;void fun1(int &num)
{//mutex_.lock();num = num +1;cout<<"线程 fun 1:"<<num<<endl;//mutex_.unlock();
}void fun2(int &num)
{//mutex_.lock();num = num * 2;cout<<"线程 fun 2:"<<num<<endl;//mutex_.unlock();
}int main()
{int num=3;std::thread a(fun1,std::ref(num));std::thread b(fun2,std::ref(num));a.join();b.join();cout<<"主线程 num:"<<num<<endl;return 0;
}
输出结果:
但我们期待的输出结果是:
线程 fun 1:4
线程 fun 2:8
主线程 num:8
出现这种情况主要是两个子线程同时对互斥量操作,导致出现了错误;所以这时候就需要使用互斥锁来实现互斥。
2.3.1 使用std::mutex
#include <iostream>
#include <thread>
#include <mutex>using namespace std;
mutex mutex_;void fun1(int &num)
{mutex_.lock();num = num +1;cout<<"线程 fun 1:"<<num<<endl;mutex_.unlock();
}void fun2(int &num)
{mutex_.lock();num = num * 2;cout<<"线程 fun 2:"<<num<<endl;mutex_.unlock();
}int main()
{int num=3;std::thread a(fun1,std::ref(num));std::thread b(fun2,std::ref(num));a.join();b.join();cout<<"主线程 num:"<<num<<endl;return 0;
}
输出结果
2.3.2 使用std::lock_guard()
std::lock_guard
的生命周期结束后,会自动执行析构函数,释放互斥量的控制权,相比于使用mutex::lock
和unlock
而言,比较方便;但是没有后者灵活。另外,std::lock_guard
可以使用{}
来控制其生命周期。
#include <iostream>
#include <thread>
#include <mutex>using namespace std;
mutex mutex_;void fun1(int &num)
{// mutex_.lock();std::lock_guard<mutex> guard1(mutex_);num = num +1;cout<<"线程 fun 1:"<<num<<endl;//mutex_.unlock();
}void fun2(int &num)
{//mutex_.lock();std::lock_guard<mutex> guard2(mutex_);num = num * 2;cout<<"线程 fun 2:"<<num<<endl;//mutex_.unlock();
}int main()
{int num=3;std::thread a(fun1,std::ref(num));std::thread b(fun2,std::ref(num));a.join();b.join();cout<<"主线程 num:"<<num<<endl;return 0;
}
输出结果:
2.3.3 使用std::unique_lock()
虽然std::lock_guard
提高了编程效率,但是使用std::lock_guard
后不能手动lock()
与手动unlock()
;如果你想在子线程的某个地方释放互斥量的控制权,std::lock_guard
就显得很吃力,为此,C++提供了std::unique_lock
,类似于std::lock_guard
,保留了其功能,同时也可以手动解锁和上锁。
std::unique_lock
支持传入参数defer_lock
和try_to_lock
,std::adopt_lock
,前者是默认不上锁,后者的用法和lock_guard
类似,需要用guard.owns_lock()
来判断是否锁住。
#include <iostream>
#include <thread>
#include <mutex>using namespace std;
mutex mutex_;void fun1(int &num)
{std::unique_lock<mutex> guard1(mutex_,std::defer_lock);guard1.lock();num = num +1;cout<<"线程 fun 1:"<<num<<endl;++num;guard1.unlock();
}void fun2(int &num)
{std::unique_lock<mutex> guard2(mutex_,std::defer_lock);guard2.lock();num = num * 2;cout<<"线程 fun 2:"<<num<<endl;num = num + 4;guard2.unlock();
}int main()
{int num=3;std::thread a(fun1,std::ref(num));std::thread b(fun2,std::ref(num));a.join();b.join();cout<<"主线程 num:"<<num<<endl;return 0;
}
执行结果:
2.4 原子操作
原子操作指不可分割的操作,只允许一个线程去操作原子变量。如果有很多个线程都要对一个变量进行操作,那么根据上面的内容,我们要不断的进行上锁和解锁,这样是非常麻烦的。原子类型的好处就体现出来了,定义一个原子变量,只允许单个线程来访问它,同时由于它的原子性,不需要上锁解锁,操作的效率和代码执行的效率更高。(包含在头文件< atomic >中)
#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>using namespace std;
mutex mutex_;
std::atomic<bool> num(true);void fun1(void)
{num = false;
}void fun2(void)
{num = true;
}int main()
{std::thread a(fun1);std::thread b(fun2);a.join();b.join();cout<<"主线程 num:"<<num<<endl;return 0;
}
《C++(三)--多线程》相关推荐
- ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)
视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...
- ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析
视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...
- 信息学奥赛真题解析(玩具谜题)
玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...
- 信息学奥赛之初赛 第1轮 讲解(01-08课)
信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...
- 信息学奥赛一本通习题答案(五)
最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...
- 信息学奥赛一本通习题答案(三)
最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...
- 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题
第1章 快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章 素数 第 3 章 约数 第 4 章 同余问题 第 5 章 矩阵乘法 第 6 章 ...
- 信息学奥赛一本通题目代码(非题库)
为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...
- 信息学奥赛一本通(C++版) 刷题 记录
总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...
- 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离
首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...
最新文章
- 金蝶加密服务器显示339错误,打开金蝶软件出现这个提示怎么解决???
- windows如何访问linux系统文件,如何从 Windows 访问 Linux 文件?——方法步骤
- linux bash错误,linux bash错误重定向输出
- python输出小数_python输出小数
- *帅帅老师,编写函数,将999-9999整数放入一个数组当中
- 黑色脸谱_上演三幕的脸谱剧
- Google Chrome 插件推荐
- Oracle 各备份恢复环境下 SCN
- 程序员买啥游戏机,自己动手做一个体感小游戏
- 实验十八 CISCO设备IOS的备份与升级
- Abp mysql guid_使用ABP框架踩过的坑系列5
- 041孙悟空第三人称视角
- 三星SDS为韩国航运业成功测试区块链技术
- oracle10g无监听配置文件,Oracle 10g学习笔记(七) tnsnames.ora 监听配置文件详解
- 假币问题 (java)居然有假币!!!
- 怎样才算得上是一名优秀的软件测试工程师呢?
- Java我的2020年终盘点
- Spring容器 SpringMVC容器 web容器的关系
- C#delegate、event、Action、EventHandler 的使用和区别
- Trace32 SRST和TRST、system.attach 和 system.up的区别