当前位置: 首页 > news >正文

C++多线程编程超详解

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从而可以实现线程的挂起唤醒

测试代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

voidthreadFunc01() {

cout <<"thread join1"<< endl;

this_thread::sleep_for(chrono::seconds(2));

}

voidthreadFunc02() {

cout <<"thread join2"<< endl;

this_thread::sleep_for(chrono::seconds(2));

}

voidtest01() {

// 创建线程

std::threadthread1(threadFunc01);

std::threadthread2(threadFunc02);

//thread.join(); //join 会阻塞主线程 同步操作

//thread.detach(); //detach 不会阻塞主线程 异步操作

boolbJoinAble = thread1.joinable();

thread::id threadId = thread1.get_id();

//hardware_concurrency 硬件并发的数量

intthreadNum = thread1.hardware_concurrency();

cout <<"hardware_concurrency:"<< threadNum << endl;

//应用 线程的预分配。

for(inti = 0; i < thread1.hardware_concurrency(); i++) {

std::threadthreadRef(threadFunc01);

threadRef.detach();

}

thread1.swap(thread2);

thread1.join();

}

向线程里传递参数的方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

// 向线程里传递参数的方法

#include<string>

voidthreadFunc03(intnum,conststring& str) {

cout <<"num = "<< num <<" str = "<< str << endl;

}

structFObject {

voidRun(conststring& str) {

cout << str << endl;

}

};

voidtest02() {

// 通过函数绑定

threadnewThread1(threadFunc03, 10,"Unreal");

newThread1.detach();

// 通过lambda绑定

inta = 50;

threadnewThread2([&](intnum,conststring& str) {

cout <<"a = "<< a <<" num = "<< num <<" str = "<< str << endl;

}, 1,"Unreal");

newThread2.detach();

// 绑定对象

FObject objectRef;

threadnewThread3(&FObject::Run, objectRef,"Unreal");

newThread3.detach();

}

2.互斥锁mutex

​ 头文件#include<mutex>

API描述注意
mutex.lock()上锁
mutex.unlock()解锁
mutex.try_lock()判断可不可以加锁,返回bool可以用该方法建立非阻塞模式

测试代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

#include<mutex>

mutex lockRef;

voidthreadFunc04(intnum,conststring& str) {

// 进入该线程锁住该线程,其他线程想要进入该线程需要排队

lockRef.lock();

cout <<"thread join4"<< endl;

this_thread::sleep_for(chrono::seconds(2));

// 解锁

lockRef.unlock();

}

voidtest03() {

std::threadthread1(threadFunc04, 10,"Unreal");

std::threadthread2(threadFunc04, 5,"Unity");

std::threadthread3(threadFunc04, 20,"Cocos");

thread1.detach();

thread2.detach();

thread3.detach();

}

使用类加锁的方式:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

#include<mutex>

mutex lockRef;

structFEvent {

FEvent() {

m.lock();

}

~FEvent()

{

m.unlock();

}

staticmutex m;

};

mutex FEvent::m;

#define LOCK_SCOPE FEvent Event

voidthreadFunc04(intnum,conststring& str) {

LOCK_SCOPE;//加上锁,并且过了这个作用域自动解锁(析构)

cout <<"thread join4"<< endl;

this_thread::sleep_for(chrono::seconds(2));

}

voidtest03() {

std::threadthread1(threadFunc04, 10,"Unreal");

std::threadthread2(threadFunc04, 5,"Unity");

std::threadthread3(threadFunc04, 20,"Cocos");

thread1.detach();

thread2.detach();

thread3.detach();

}

try_lock()

1

2

3

4

5

6

7

8

voidthreadFunc04(intnum,conststring& str) {

boolbLock = FEvent::m.try_lock();

if(bLock) {

LOCK_SCOPE;//加上锁,并且过了这个作用域自动解锁(析构)

cout <<"thread join4"<< endl;

this_thread::sleep_for(chrono::seconds(2));

}

}

​ 使用try_lock()可以进行判断能不能上锁,不能上锁的话,就不用执行上锁后的代码,防止其他线程阻塞在该线程。

lock_guard

lock_guard是一种锁类,作用和我们上面自定义的锁类FEvent相同,创建的时候锁住目标线程,释放的时候解锁。

1

2

// 声明方式

lock_guard<mutex>ref;

源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

template<class_Mutex>

classlock_guard {// class with destructor that unlocks a mutex

public:

usingmutex_type = _Mutex;

explicitlock_guard(_Mutex& _Mtx) : _MyMutex(_Mtx) {// construct and lock

_MyMutex.lock();

}

lock_guard(_Mutex& _Mtx, adopt_lock_t) : _MyMutex(_Mtx) {// construct but don't lock

}

~lock_guard() noexcept {

_MyMutex.unlock();

}

lock_guard(constlock_guard&) =delete;

lock_guard& operator=(constlock_guard&) =delete;

private:

_Mutex& _MyMutex;

};

unique_lock

​ 作用和lock_guard相同,唯一的不同之处,lock_guard开放的API只有析构函数,而unique_lock开放的API非常多,即自由度比lock_guard高,可以定义锁的行为。

1

2

3

4

5

6

7

8

9

10

11

12

voidtest05() {

// defer_lock 关键字为延迟锁,即创建该对象时不会锁住该线程,什么时候锁需要自定义

std::unique_lock<mutex>lockRef2(FEvent::m,defer_lock);

std::unique_lock<mutex>lockRef2(FEvent::m,chrono::seconds(2));//锁两秒

//....执行

lockRef2.lock();

lockRef2.unlock();

boolbLock1 = lockRef2.try_lock();//尝试上锁

lockRef2.try_lock_for(chrono::seconds(2));//锁2s

mutex *lockRef3 = lockRef2.release();//释放锁,同时会返回被释放的这个锁的指针对象

boolbLock2 = lockRef2.owns_lock();//当前是否被锁住

}

应用:

1

2

3

4

5

6

7

8

9

10

11

12

voidtest05() {

//std::lock_guard<mutex>lockRef1(FEvent::m);

// defer_lock 关键字为延迟锁

std::unique_lock<mutex>lockRef2(FEvent::m,defer_lock);

lockRef2.lock();

lockRef2.mutex();

boolbLock = lockRef2.owns_lock();

std::unique_lock<mutex>lockRef3;

lockRef2.swap(lockRef3);

std::unique_lock<mutex>lockRef4 = move(lockRef3);

lockRef4.unlock();

}

3. 挂起和唤醒

​ 头文件#include<windows.h>

1111111

111111

111111

11111

111111

测试代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

#include<windows.h>

voidthreadFunc05() {

while(true)

{

Sleep(10);

cout <<"threadFunc05"<< endl;

}

}

voidtest04() {

threadthread1(threadFunc05);

// 挂起线程

SuspendThread(thread1.native_handle());

Sleep(2);

// 唤醒线程

ResumeThread(thread1.native_handle());

}

如何高效将主线程资源进行转移:

1

2

3

4

5

6

7

8

9

10

voidthreadFunc06(constchar* str) {

cout << str << endl;

}

voidtest04() {

// 如何高效转移线程资源

// 使用std::move

threadthread2(threadFunc06, move("Unreal"));// 使用move避免了拷贝

threadthread3 = move(thread2);

thread3.detach();

}

3. 应用场景

3.1 call_once执行一次的函数

​ 通过使用该函数,用来防止多线程的多次触发。

1

2

3

4

5

6

7

8

9

10

11

12

once_flag tag;

voidcallonceTest() {

call_once(tag, [&]() {

cout <<"Do once"<< endl;

});

}

voidtest06() {

for(inti = 0; i < 10; i++) {

threadthread1(callonceTest);

thread1.detach();

}

}

3.2 condition_variable条件锁

​ 使用需要包含头文件#include<condition_variable>

可以使用条件锁来达到同步的作用,即当满足一定的条件后才解锁某个线程。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

#include<condition_variable>

condition_variable condition_lock;

mutex mutexLock;

voidconditionFuncTest() {

unique_lock<mutex>lock(mutexLock);

condition_lock.wait(lock);//锁住该线程

cout <<"Run"<< endl;

}

voidtest12() {

std::threadthreadRef(conditionFuncTest);

threadRef.detach();

Sleep(3000);//3s后再激活

condition_lock.notify_one();

}

3.3 future获取线程的计算结果

​ 通过使用future可以得到"未来"线程被调用的时候计算得返回值,使用时需要包含头文件#include<future>。

声明方式:

1

2

// async为创建该线程的方式为异步 funName 函数名 args为传入的函数参数

std::future<string>newFuture = std::async(launch::async, funName,args...);

应用:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

#include<future>

string getString(intnum) {

return"Unreal";

}

voidtest08() {

std::future<string>newFuture = std::async(launch::async, getString, 10);

//std::future<string>newFuture = std::async(launch::deferred, getString, 10); // 睡一秒再执行

Sleep(1000);

string str = newFuture.get();//get只能调用一次 调第二次会崩溃

// 防止崩溃的写法

if(newFuture.valid()) {

string str = newFuture.get();

}

}

3.4 promise主线程如何将数据发送数据到其他线程

​ 通过使用promise(承诺)来进行进程之间的交互,常配合std::future使用。其作用是在一个线程t1中保存一个类型typename T的值,可供相绑定的std::future对象在另一线程t2中获取。

​ 测试代码:

1

2

3

4

5

6

7

8

9

10

11

// promise

string promiseTest(future<string>& future) {

cout << future.get() << endl;

return"Unreal";

}

voidtest09() {

promise<string> promiseRef;

future<string>future1 = promiseRef.get_future();

future<string>future2 = std::async(launch::async, promiseTest, std::ref(future1));//future 不支持值拷贝 需要传递引用

promiseRef.set_value("Unreal is the best game engine in the world");

}

​ 但这里也有一个问题需要思考,如果需要发送数据到多个线程,是不是需要一个个的创建上面的代码呢。这里就引出了多线程之间共享状态这个解决方法。

3.5 future.share()多线程之间共享状态

​ 通过future.share()我们可以很方便的使多个线程之间共享状态。

现在来看看没有使用该函数的话我们要共享状态的话需要这么写:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

string promiseTest(future<string>& future) {

cout << future.get() << endl;

return"Unreal";

}

voidtest09() {

promise<string> promiseRef;

future<string>future1 = promiseRef.get_future();

future<string>future2 = promiseRef.get_future();

future<string>future3 = promiseRef.get_future();

future<string>future4 = std::async(launch::async, promiseTest, std::ref(future1));//future 不支持值拷贝 需要传递引用

future<string>future5 = std::async(launch::async, promiseTest, std::ref(future2));//future 不支持值拷贝 需要传递引用

future<string>future6 = std::async(launch::async, promiseTest, std::ref(future3));//future 不支持值拷贝 需要传递引用

promiseRef.set_value("Unreal is the best game engine in the world");

}

使用了future.share()函数后:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

string promiseTest02(shared_future<string> future) {

cout << future.get() << endl;

return"Unreal";

}

voidtest09() {

promise<string> promiseRef;

future<string>future1 = promiseRef.get_future();

// shared_future

shared_future<string> sharedFutrue1 = future1.share();

future<string>future2 = std::async(launch::async, promiseTest02, sharedFutrue1);//shared_future 可以用拷贝传递

future<string>future3 = std::async(launch::async, promiseTest02, sharedFutrue1);

future<string>future4 = std::async(launch::async, promiseTest02, sharedFutrue1);

promiseRef.set_value("Unreal is the best game engine in the world");

}

3.6 线程packaged_task

​ packaged_taskpromise非常相似,packaged_task<F>是对promise<T= std::function<F>>中T= std::function<F>这一可调对象(如函数、lambda表达式等)进行了包装,简化了使用方法。并将这一可调对象的返回结果传递给关联的future对象。

绑定Lambda

1

2

3

4

5

6

7

8

9

10

11

12

voidtest10() {

//绑定lambda

packaged_task<int(int,int)> task1([](inta,intb) ->int{

returna + b;

});

task1(1, 4);

this_thread::sleep_for(chrono::seconds(1));

if(task1.valid()) {

auto f1 = task1.get_future();

cout << f1.get() << endl;

}

}

绑定普通函数

1

2

3

4

5

6

7

8

9

10

11

12

13

intpackagedTest(inta,intb) {

returna + b;

}

voidtest10() {

//绑定函数

packaged_task<int(int,int)>task2(packagedTest);

task2(10, 5);

this_thread::sleep_for(chrono::seconds(1));

if(task2.valid()) {

auto f2 = task2.get_future();

cout << f2.get() << endl;

}

}

使用std::bind进行函数绑定

1

2

3

4

5

6

7

8

9

10

11

12

13

intpackagedTest(inta,intb) {

returna + b;

}

voidtest10() {

// bind

packaged_task<int(int,int)>task3(std::bind(packagedTest,1,2));

task3(10, 5);//因为bind使用了占位符 所以这里传入的10 5失效了

this_thread::sleep_for(chrono::seconds(1));

if(task3.valid()) {

auto f3 = task3.get_future();

cout << f3.get() << endl;//1+2

}

}

3.7 时间约束

1

2

3

4

5

6

7

voidtest11() {

//休眠2s

this_thread::sleep_for(chrono::seconds(2));

// 休眠现在的时间加上2s

chrono::steady_clock::time_point timePos = chrono::steady_clock::now() + chrono::seconds(2);

this_thread::sleep_until(timePos);

}

4. Windows多线程

​ 使用WindowsAPI进行多线程的编写,需要包含头文件

1

#include<windows.h>

4.1 Windows创建线程

​ 使用CreateThread()创建线程

1

2

3

4

5

6

7

8

9

10

11

12

DWORDWINAPI funcThread(LPVOIDlpPram) {

// DWORD 类型为unsigned long

// LPVOID 类型为void

cout <<"Unreal!"<< endl;

Sleep(1000);

return0l;

}

voidwindowsThreadTest01() {

HANDLEhandleRef = CreateThread(nullptr,0, funcThread,nullptr,0,nullptr);

Sleep(2000);

CloseHandle(handleRef);//使用之后需要关闭handle

}

​ 其中传入的参数为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/*

WINBASEAPI

_Ret_maybenull_

HANDLE

WINAPI

CreateThread(

_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, 和线程安全有关 一般为null

_In_ SIZE_T dwStackSize, 线程栈的大小

_In_ LPTHREAD_START_ROUTINE lpStartAddress, 被线程执行的回调函数

_In_opt_ __drv_aliasesMem LPVOID lpParameter, 传入线程的参数

_In_ DWORD dwCreationFlags, 创建线程的标志 参数0 代表立即启动该线程

_Out_opt_ LPDWORD lpThreadId 传出的线程ID

);

*/

4.2 Windows互斥锁

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// windows互斥锁

HANDLEhMutex = nullptr;

DWORDWINAPI funcThread02(LPVOIDlpParam) {

cout <<"Unreal"<< endl;

WaitForSingleObject(hMutex, INFINITE);

Sleep(5000);

ReleaseMutex(hMutex);

return0l;

}

voidwindowsThreadTest02() {

hMutex = CreateMutex(nullptr,false, L"Mutex");

HANDLEhandleRef1 = CreateThread(nullptr, 0, funcThread02, nullptr, 0, nullptr);

HANDLEhandleRef2 = CreateThread(nullptr, 0, funcThread02, nullptr, 0, nullptr);

CloseHandle(handleRef1);

CloseHandle(handleRef2);

}

传入的参数为:

1

2

3

4

5

6

7

8

9

10

11

/*

WINBASEAPI

_Ret_maybenull_

HANDLE

WINAPI

CreateMutexW(

_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, 和线程安全有关一般为null

_In_ BOOL bInitialOwner, 有没有该锁的控制权

_In_opt_ LPCWSTR lpName 锁名字

);

*/

4.3 Windows挂起和唤醒线程

​ 通过使用SuspendThread(HandleRef)和ResumeThread(HandleRef)来挂起和唤醒线程

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// windows 挂起唤醒

DWORDWINAPI funcThread03(LPVOIDlpParam) {

while(true) {

Sleep(500);

cout <<"IsRunning"<< endl;

}

return0l;

}

voidwindowsThreadTest03() {

HANDLEhRef = CreateThread(nullptr, 0, funcThread03, nullptr, 0, nullptr);

SuspendThread(hRef);

Sleep(2000);

ResumeThread(hRef);

CloseHandle(hRef);

}

总结

本篇文章就到这里了,希望能够给你带来帮助

http://www.jsqmd.com/news/1048778/

相关文章:

  • VMware Workstation Pro 17免费许可证密钥:终极实战指南与完整资源库
  • Noto字体:彻底解决全球900+语言显示问题的终极方案
  • 2026 年沧州厨卫屋顶防水修缮三家对比测评 吉修匠 99.8 分稳居榜首 - 吉修匠
  • 2026贵金属回收测评白皮书 沈阳黄金回收排名合扬实力夺冠 - 奢侈品交易观察员
  • 福州家电维修平台推荐:本地用户反馈较好的几家服务商深度实测对比——2026年6月最新发布 - 一步到家
  • QQScreenShot独立版:告别登录烦恼,体验极致截图体验的终极指南 [特殊字符]
  • OpenClaw:轻量级AI网关与多模型路由中枢实战指南
  • 六安性价比高的生日蛋糕哪家好吃?6家门店真实价格品质测评 - 速递信息
  • 抖音有运营扶持的公会哪家好 - 速递信息
  • 终极串口调试工具SuperCom:一站式多串口管理与自动化测试解决方案
  • 如何高效批量下载抖音无水印视频:5分钟掌握专业工具完整指南
  • PDown下载器:2024年解决百度网盘限速问题的终极方案
  • LaserGRBL深度解析:5大核心功能如何革新激光雕刻工作流
  • 抖音无水印视频下载终极指南:3步实现纯净高清保存
  • 深入解析NXP LPC2100系列ARM7微控制器:架构、外设与实战应用
  • 2026 年 6 月同步更新|珠海理查德米勒官方授权售后表带定制更换,珠海理查德米勒异形腕表该原厂橡胶带还是手工定制表带? - 亨得利官方维修中心
  • OpenPLC Editor完整指南:5步掌握免费工业自动化编程
  • 如何用LinkSwift实现网盘直链下载?3步免费解锁高速下载体验!
  • 基于Python的Vulnx漏洞扫描报告自动化生成实战
  • 超牛掰的工程化Skills开发必备之初始化你的AI coding Agent环境
  • 2026 盐城放心收金清单:四家正规门店全维度实测,徐靠谱综合榜首 - 速递信息
  • 2026扬州全屋定制可丽芙授权本地靠谱商家整理 - 十大品牌排行榜
  • 证件照换底色怎么换才自然?2026免费AI换底色工具发丝级实测对比 - 科技大爆炸
  • 一文吃透 2026 大润发购物卡回收规则,省心盘活闲置卡券 - 京卡收卡券回收
  • 深入解析ColdFire内核异常处理与指令时序:嵌入式系统稳定与性能优化指南
  • 2025-2026年湖北尊而光律师事务所刑事团队电话查询:委托前请核实资质与收费标准 - 品牌推荐
  • LPC5411x异构双核MCU实战:架构解析、外设应用与低功耗设计
  • 2025-2026年青岛全程源机械有限公司电话查询:铸造装备选型需综合评估技术参数与售后服务 - 品牌推荐
  • 2026 年张家口厨卫屋顶防水修缮三家对比测评 吉修匠 99.8 分稳居榜首 - 吉修匠
  • 全国500+直营门店!2026合扬领跑沈阳黄金回收市场 - 奢侈品交易观察员