C++多线程编程详解
C++多线程
1. 概念
1.1 概念
- 进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。
- 线程:进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
- 并发:并发指的是两个或多个独立的活动在同一时段内发生。并发在生活中随处可见:比如在跑步的时候同时听音乐,在看电脑显示器的同时敲击键盘等。同一时间段内可以交替处理多个操作,强调同一时段内交替发生。
- 并行:同一时刻内同时处理多个操作,强调同一时刻点同时发生。
2. 常用API
头文件#include<thread>
1.thread
API | 描述 | 注意 |
---|---|---|
thread.join() | 加入线程(会阻塞主线程,模拟同步操作) | |
thread.detach() | 加入线程(不会阻塞主线程,模拟异步操作) | |
thread.joinable() | 是否可加入线程,返回bool | |
thread.get_id() | 获取线程的ID | |
thread.hardware_concurrency() | 获取硬件并发的数量 | |
thread.swap() | 交换线程 | |
thread.native_handle() | 获取原生handle,为windows多线程中CreateThread的返回值,使用这个handle从而可以实现线程的挂起唤醒 |
测试代码:
向线程里传递参数的方法:
2.互斥锁mutex
头文件#include<mutex>
API | 描述 | 注意 |
---|---|---|
mutex.lock() | 上锁 | |
mutex.unlock() | 解锁 | |
mutex.try_lock() | 判断可不可以加锁,返回bool | 可以用该方法建立非阻塞模式 |
测试代码:
使用类加锁的方式:
try_lock()
使用try_lock()
可以进行判断能不能上锁,不能上锁的话,就不用执行上锁后的代码,防止其他线程阻塞在该线程。
lock_guard
lock_guard
是一种锁类,作用和我们上面自定义的锁类FEvent
相同,创建的时候锁住目标线程,释放的时候解锁。
源码:
unique_lock
作用和lock_guard
相同,唯一的不同之处,lock_guard
开放的API
只有析构函数,而unique_lock
开放的API非常多,即自由度比lock_guard
高,可以定义锁的行为。
应用:
3. 挂起和唤醒
头文件#include<windows.h>
API | 描述 | 注意 |
---|---|---|
SuspendThread(thread.native_hadle()) | 挂起线程 | |
ResumeThread(thread.native_hadle()) | 唤醒线程 | |
Sleep() | 睡眠 |
测试代码:
如何高效将主线程资源进行转移:
3. 应用场景
3.1 call_once执行一次的函数
通过使用该函数,用来防止多线程的多次触发。
3.2 condition_variable条件锁
使用需要包含头文件#include<condition_variable>
可以使用条件锁来达到同步的作用,即当满足一定的条件后才解锁某个线程。
3.3 future获取线程的计算结果
通过使用future可以得到"未来"线程被调用的时候计算得返回值,使用时需要包含头文件#include<future>。
声明方式:
应用:
3.4 promise主线程如何将数据发送数据到其他线程
通过使用promise(承诺)
来进行进程之间的交互,常配合std::future
使用。其作用是在一个线程t1中保存一个类型typename T的值,可供相绑定的std::future
对象在另一线程t2中获取。
测试代码:
但这里也有一个问题需要思考,如果需要发送数据到多个线程,是不是需要一个个的创建上面的代码呢。这里就引出了多线程之间共享状态这个解决方法。
3.5 future.share()多线程之间共享状态
通过future.share()我们可以很方便的使多个线程之间共享状态。
现在来看看没有使用该函数的话我们要共享状态的话需要这么写:
使用了future.share()
函数后:
3.6 线程packaged_task
packaged_task
和promise
非常相似,packaged_task<F>
是对promise<T= std::function<F>>中T= std::function<F>
这一可调对象(如函数、lambda
表达式等)进行了包装,简化了使用方法。并将这一可调对象的返回结果传递给关联的future对象。
绑定Lambda
绑定普通函数
使用std::bind
进行函数绑定
3.7 时间约束
4. Windows多线程
使用WindowsAPI
进行多线程的编写,需要包含头文件
4.1 Windows创建线程
使用CreateThread()
创建线程
其中传入的参数为:
4.2 Windows互斥锁
传入的参数为:
4.3 Windows挂起和唤醒线程
通过使用SuspendThread(HandleRef)
和ResumeThread(HandleRef)
来挂起和唤醒线程
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程学习网的更多内容!