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

C++ 多线程(一)

C++ 多线程(一)

  • 1.std中的thread API 介绍
    • 开启一个线程
    • 获取线程信息API
    • 交换两个线程
  • 2.向线程里传递参数的方法
    • 第一种方式(在创建线程的构造函数后携带参数)
    • 第二种方式(Lambda)
    • 第三种方式(成员函数)
  • 3.互斥锁
  • 4.try_lock
  • 5.挂起和唤醒线程
  • 6.将主线程资源移动到其他线程
  • 7.lock_guard
  • 8.unique_lock
  • 9.call_once
  • 10.condition_variable


1.std中的thread API 介绍

开启一个线程

如下是定义一个线程

std::thread Thread1(ThreadFunc1)

其开始运行有两种方式,一个是阻塞当前调用进程,一个是异步的方式不会阻塞当前调用的进程

join会阻塞当前调用的进程

Thread1.join()

detach不会阻塞当前调用进程

Thread1.detach()

获取线程信息API

获取线程ID

Thread1.get_id()

判断线程是否是可启用的

Thread1.joinable() 

这样输出的值是true

	std::thread Thread1(ThreadFunc1);cout << Thread1.joinable() << endl;

如果传入的执行参数为空则是不可启用

	std::thread Thread1;cout << Thread1.joinable() << endl;

如果想获取计算机能创建的线程数

Thread1.hardware_concurrency()

获取当前线程ID

 std::this_thread::get_id()

使当前线程睡眠n秒
有两种方式,一种是std的,一种是windows底层的

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

注意windows的睡眠需要引用头文件Windows.h

#include <Windows.h>
Sleep(1000);

交换两个线程

std::thread Thread1(ThreadFunc1)
std::thread Thread2(ThreadFunc2)
Thread1.spawn(Thread2)

2.向线程里传递参数的方法

先定义要在子线程执行的方法

void Func1(int a, const string& b)
{cout << a << endl;cout << b << endl;
}

第一种方式(在创建线程的构造函数后携带参数)

	std::thread NewThread1(Func1, 1, "Hello");NewThread1.join();

第二种方式(Lambda)

	int a = 100;std::thread NewThread2([&](int value1,const string& value2){cout << "=====================" << endl;cout << a << endl;cout << value1 << endl;cout << value2 << endl;},2,"World");NewThread2.join();

第三种方式(成员函数)

class FTestClass
{
public:void Run(int a,const string& b){cout << a << endl;cout << b << endl;}
};
	FTestClass TestClass;std::thread NewThread3(&FTestClass::Run, &TestClass, 3, "TestClass");NewThread3.join();

3.互斥锁

为了避免多线程之间的资源竞争自然需要这种互斥的锁

使用前需要引用头文件

#include <mutex>
mutex mx;
void NewThreadFunc()
{mx.lock();cout << "等待2s" << endl;std::this_thread::sleep_for(chrono::seconds(2));mx.unlock();
}
	for (size_t i = 0; i < 5; i++){std::thread th(NewThreadFunc5);th.detach();}

最后我们会发现每隔2s输出一次信息

我们每次使用锁都需要lock 和 unlock 这是十分不便利的,而且如果我们忘记解锁就会死锁
我们可以使用析构来实现这种自动解锁的方式

struct FEvent
{FEvent(){m.lock();}~FEvent(){m.unlock();}static mutex m;
};
mutex FEvent::m;

再使用宏包裹一下

#define LOCK_TEST FEvent LockEvent;

如下是在线程执行的函数,只需要定义一行就可以自动解锁

void NewThreadFunc()
{LOCK_TESTcout << "等待2s" << endl;std::this_thread::sleep_for(chrono::seconds(2));
}

4.try_lock

try_lock 返回bool 值,是否能上锁

static mutex mx;
void NewThreadFunc2()
{if (mx.try_lock()){cout << "等待2s" << endl;std::this_thread::sleep_for(chrono::seconds(2));mx.unlock();}else {cout << "锁被使用" << endl;}
}
	for (size_t i = 0; i < 5; i++){std::thread NewThread2(NewThreadFunc2);NewThread2.detach();}

最后输出我们会发现只打印了一个等待2s 和四个锁被使用

5.挂起和唤醒线程

定义在子线程执行的函数

void NewThreadFunc3()
{while (true){Sleep(1000);cout << "Hello" << endl;}
}

SuspendThread 用于挂起线程
ResumeThread 用于唤醒线程

	std::thread th(NewThreadFunc3);SuspendThread(th.native_handle());std::this_thread::sleep_for(chrono::seconds(2));ResumeThread(th.native_handle());

6.将主线程资源移动到其他线程

定义在子线程执行的函数

void NewThreadFunc4(const string& Value)
{cout << Value << endl;
}
	std::thread th1(NewThreadFunc4,move("Hello world"));std::thread th2 = move(th1);th2.detach();

将th1的所有权转移给th2。此时,th1不再代表任何线程(相当于th1处于“空”状态),而th2现在代表原来th1所代表的线程。这是因为std::thread是不可复制的,但可以移动。所以,这里通过移动赋值(或移动构造)将th1的线程所有权转移给th2。

7.lock_guard

类似我们之前自己封装的自动解锁的锁,不要手动调用unlock,函数执行完毕自动析构

mutex mx;
void NewThreadFunc5()
{lock_guard<mutex> lock(mx);cout << "Hello world" << endl;Sleep(2000);
}
	for (size_t i = 0; i < 5; i++){std::thread th(NewThreadFunc5);th.detach();}

每隔2s输出一次一共输出了5次

8.unique_lock

unique_lock 相对于上面的 lock_guard 多了更多的功能API 可以更自由丰富的操作锁

defer_lock 此参数是延时的意思,锁并不是立马生效,需要手动lock

unique_lock 也是过了作用域会自动解锁

static mutex mt;
void Func1()
{unique_lock<mutex> lock1(mt,defer_lock);// 不需要上锁的函数cout << "这是不需要上锁的函数" << endl;lock1.lock();cout << "这是需要上锁的函数" << endl;lock1.unlock();//lock2.try_lock_until(); 锁到某个时间点//lock2.release(); 释放//lock2.mutex();拿到锁本体//lock2.swap(); 交换//lock2.owns_lock(); 判断是否锁住
}

下面演示一个unique_lock 的 try_lock_for()方法

static timed_mutex timeMt;
void Func2()
{unique_lock<timed_mutex> lock2(timeMt, defer_lock);if (lock2.try_lock_for(chrono::seconds(2))){cout << "锁住2s后" << endl;this_thread::sleep_for(chrono::seconds(1));}else{cout << "锁正在被占用" << endl;}
}
	for (size_t i = 0; i < 5; i++){thread th(Func2);th.detach();}

我们会发现输出了2个锁住2s后和3个锁正在被占用,是因为这个锁定义了锁住的时间为2s,当第一次运行后只执行了1s第二次进入仍然没超过定义的锁的2s所以可以进入,而之后都超过2s了,故无法进入了。

9.call_once

顾名思义就是无论调用多少次只执行一次

once_flag oneFlag;
void Func3()
{call_once(oneFlag, [](){cout << "运行一次" << endl;});
}
	for (size_t i = 0; i < 5; i++){Func3();}

10.condition_variable

条件变量,一个地方可以等待直到通知这个等待就可以执行等待之后的代码

mutex tx1;
condition_variable cv;
void Func4()
{Sleep(1000);cv.notify_one();Sleep(1000);
}
	std::thread th(Func4);th.detach();unique_lock<mutex> Lock1(tx1);cv.wait(Lock1);cout << "运行Wait之后" << endl;

notify_one 是一个,还有多个版本notify_all

http://www.dtcms.com/a/300517.html

相关文章:

  • 详解力扣高频SQL50题之610. 判断三角形【简单】
  • Vscode的常用快捷键(摆脱鼠标计划)
  • [N1盒子] 斐讯盒子N1 T1通用刷机包(可救砖)
  • 金字塔降低采样
  • C语言:顺序表(上)
  • K8S 九 安全认证 TLS
  • 关于西门子博图基本指令的应用区别
  • VScode 支持 QNX 源码跳转
  • 【Python系列】从内存分析到性能剖析
  • Mysql 二进制安装常见问题
  • 2025年Solar应急响应公益月赛-7月wp
  • mac neo4j install verifcation
  • 论文阅读-IGEV
  • SecureCRT连接密钥交换失败
  • 基于LNMP架构的分布式个人博客搭建
  • 总结和对比Unity中的三种主要抗锯齿技术:FXAA、SMAA和TAA
  • 搭建DM数据守护集群
  • Java 代理机制详解:从静态代理到动态代理,彻底掌握代理模式的原理与实战
  • 【服务器与部署 30】Python内存优化实战:从内存泄漏到性能提升的完整解决方案
  • VLA-视觉语言动作模型
  • mac配置多版本jdk
  • 四、搭建springCloudAlibaba2021.1版本分布式微服务-加入openFeign远程调用和sentinel流量控制
  • git stash 命令详解
  • Python 程序设计讲义(24):循环结构——循环后处理 while ... as 与 for...as
  • 大模型算法面试笔记——常用优化器SGD,Momentum,Adagrad,RMSProp,Adam
  • 算法思维进阶 力扣 300.最长递增子序列 暴力搜索 记忆化搜索 DFS 动态规划 C++详细算法解析 每日一题
  • 用KNN实现手写数字识别:基于 OpenCV 和 scikit-learn 的实战教学 (超级超级超级简单)
  • Torchv Unstrustured 文档解析库
  • Mac配置本地邮件
  • 【Qt开发】信号与槽(二)-> 信号和槽的使用