C++11新特性(5):多线程

学习C++11,根据网上资料的知识总结。

1. 线程创建

1.1 初始函数

#include <iostream>
#include <thread>
void myfunc(int &a) 
{
	cout << "a in myfunc:" << a++ << endl;
}

int main()
{
	int a = 1;

	std::thread mythread(myfunc, std::ref(a)); //带引用参数
	mythread.join();//阻塞

	cout << "a in main:" << a << endl;

	system("pause");
	return 0;
}

运行结果:

1.2 类对象

      需要重载()运算符,不然编译错误。

#include <iostream>
#include <thread>
class Task
{
	static int a;
private:
	int v;
public:
	Task() 
	{
		v = a++;
	}
public:
	void operator()()  //()重载
	{
		cout << "v:" << v<< endl;
	}
};
int Task::a = 0;
int main()
{
	for (int i = 0; i < 4; i++)
	{
		Task task;
		thread t(task);
		t.detach();
	}

	system("pause");
	return 0;
}

    运行结果不唯一,因为没有阻塞:

1.3 Lambda表达式

#include <thread>
int main()
{
	auto myLambda = [](int *a){
		for (int i = 0; i < 10; ++i)
		{
			cout << *a << endl;
		}
	};

	[myLambda]{
		int a = 10;
		thread mythread(myLambda,&a);
		mythread.detach();
	}();

	system("pause");
	return 0;
}

  运行结果:

        在Lambda线程中使用了局部变量a的指针,并且将该线程的运行方式设置为detach。这样,在lambda表达式执行结束后,变量a已经被销毁,但后台运行的线程仍然在使用已销毁变量a的指针,因此后面输出的值都是错误的。

        以detach方式执行线程时,最好将线程访问的局部数据复制到线程的空间(使用值传递),确保线程没有使用局部变量的引用或者指针。若一定要用,除非你能肯定该线程会在局部作用域结束前执行结束。使用join方式(阻塞)就不会出现这种问题,它会在作用域结束前完成退出。

1.4 std::async

      std::async是基于任务的,内部有调度器,比线程更高级别的抽象,可以看作是thread + packaged_task的封装。

原型async(std::launch::async | std::launch::deferred, f, args...)
第一个参数std::launch::deferred延迟调用,延迟到future对象调用get()或者wait()的时候才执行线程函数f
std::launch::async强制创建新线程,可能失败
std::launch::async |std::launch::deferred 默认值,系统会自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行

       std::future与std::async配合使用。std::future提供了一个访问异步操作结果的机制,它和线程是一个级别的,属于低层次的对象。在它之上高一层的是std::packaged_task和std::promise,它们内部都有future以便访问异步操作结果。std::packaged_task包装的是一个异步操作(函数),std::promise包装的是一个值;都是为了方便异步操作的。

future_status三种状态
deferred异步操作还没开始
ready异步操作已经完成
timeout异步操作超时
获取结果三种方式
get等待异步操作结束并返回结果
wait等待异步操作结束,没有返回值
wait_for超时等待返回结果

代码示例:

#include <future>
#include <iostream>

using namespace std;

int main()
{
	future<int> future1 = async(launch::async, []() { return 6; });
	cout << future1.get() << endl;  //output: 6

	future<void> future2 = async(launch::async, []() {  cout << 8 << endl; });
	cout << "before wait" << endl;
	future2.wait(); //output: 8
	cout << "after wait" << endl;

	future<int> future3 = async(launch::async, []() {
		this_thread::sleep_for(chrono::seconds(3));
	return 9;
		});

	cout << "waiting...\n";
	future_status status;
	do {
		//查询状态
		status = future3.wait_for(chrono::seconds(1));

		if (status == future_status::deferred) {
			cout << "deferred\n";
		}
		else if (status == future_status::timeout) {
			cout << "timeout\n";
		}
		else if (status == future_status::ready) {
			cout << "ready!\n";
		}
	} while (status != future_status::ready);

	cout << "result is " << future3.get() << endl;
}

运行结果:

 before wait后面的数字8是future2输出的,还没来得及换行,说明确实是多线程在输出。

2. 线程同步

2.1 互斥锁

锁类型作用
std::mutex独占的互斥锁,不能递归使用
std::timed_mutex带超时的独占互斥锁,不能递归使用
std::recursive_mutex递归互斥锁,不带超时功能
std::recursive_timed_mutex带超时的递归互斥锁

       基本用法如下,建议使用C++11新增的模板类lock_guard,可以简化互斥锁 lock()和unlock()的写法,同时也更安全。还有一个模板类是unique_lock,基本用法和lock_guard一样,提供了更多构造函数,但更占资源。

#include <mutex>
std::mutex _mutex;
_mutex.lock();//加锁,阻塞
_mutex.unlock();//解锁
_mutex.try_lock();//尝试加锁,成功返回bool,失败返回false不阻塞
或
std::lock_guard<std::mutex> lock_mtx(_mutex);

2.2 条件变量

       条件变量需要和互斥量配合使用,属于另一种用于等待的同步机制,能阻塞一个或多个线程,直到收到另一个线程发出的通知或超时时,才能唤醒当前阻塞的线程。

使用
condition_variable配合 std::unique_lock<std::mutex> 进行 wait 操作,也就是阻塞线程的操作
conditon_variable_any2.1里的四种锁

2.3 自旋锁

       自旋锁是一种忙等待形式的锁,会在用户态不停的询问锁是否可以获取(获取不到一直循环),不会陷入到内核态中,所以更加高效,但是可能会对CPU资源造成浪费。C++11中没有直接提供自旋锁的实现,但提供了原子操作的实现,可以借助原子操作实现简单的自旋锁。

       相比互斥锁,自旋锁效率更高,但是长时间的自旋可能会使CPU得不到充分的应用。在临界区代码较少,执行速度快的时候应该使用自旋锁。而互斥锁不会浪费CPU资源,在无法获得锁时使线程阻塞,将CPU让给其他线程使用。对于等待资源时间较长的场景,应该用互斥锁。

代码示例:

#include <iostream>
#include <thread>
#include <atomic>

std::atomic_flag flag;
int a = 0;
void foo()
{
	for (int i = 0; i < 100; ++i)
	{
		while (flag.test_and_set())
		{

		}//加锁
		a += 1;
		flag.clear();//解锁
	}
}
int main()
{
	flag.clear();//初始化为clear状态

	std::thread t1(foo);
	std::thread t2(foo);
	t1.join();
	t2.join();
	std::cout << a << std::endl;
	return 0;
}

a. test_and_set:返回该atomic_flag对象当前状态,检查flag是否被设置,若被设置直接返回true,若没有设置则设置flag为true后再返回false。

b. clear:清除atomic_flag对象的标志位,即设置atomic_flag的值为false。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/34024.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C1. Make Nonzero Sum (easy version) - 思维

分析&#xff1a; n一但是奇数就一定不行&#xff0c;因为无论有多少-1和1都会导致最后的和是-1或1&#xff0c;每次断开一个区间会改变2&#xff0c;所以一定不行&#xff0c;直接输出-1。 其次&#xff0c;如果数组满足题意也可以一个一个输出每一个下标&#xff0c;遍历数组…

flask socket版本问题

1、版本问题 问题解决 根据官方给定的兼容版本&#xff0c;从socket.io官网CDN下载最新的4.4.1版本js文件&#xff0c;https://cdn.socket.io/。 python-engineio使用版本。需要更新的javascript.socketio包&#xff0c;具体可对照官方文档Requirements部分末尾 https://flask-…

ansible实训-Day3(playbook的原理、结构及其基本使用)

一、前言 该篇是对ansible实训第三天内容的归纳总结&#xff0c;主要包括playbook组件的原理、结构及其基本使用方式。 二、Playbook 原理 Playbook是Ansible的核心组件之一&#xff0c;它是用于定义任务和配置的自动化脚本。 Ansible Playbook使用YAML语法编写&#xff0c;可…

帆软 FineReport 绘制漏斗图

七一建党节&#xff0c;祝党生日快乐&#xff01; 夏日炎炎&#xff0c;周末在家&#xff0c;想起在用帆软做页面展示的时候&#xff0c;使用到了漏斗图&#xff0c;记录下来&#xff0c;方便查看。 以订单销量变化为例&#xff0c;分为五个阶段&#xff0c;商品浏览人数&#…

PDF如何转换成Word?PDF转Word方法分享!​

PDF大家都不陌生了吧&#xff1f;作为打工人&#xff0c;学生党的大家都知道&#xff0c;PDF是现在不可或缺的文件传输工具之一&#xff0c;不仅可将文档转为Word&#xff0c;还可以转成excel,ppt等各种形式&#xff0c;其重要性不言而喻&#xff0c;那么今天小编就跟大家具体说…

【MySQL】表的约束

目录 一、空属性 二、默认值 三、列描述 四、zerofill 五、主键 六、自增长 七、唯一键 八、外键 九、综合案例 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻…

NSQ 实现逻辑探秘

1 什么是 NSQ NSQ 是一个消息队列中间件&#xff0c;用 go 实现&#xff0c;有如下特点&#xff1a; 分布式&#xff1a; 它提供了分布式的、去中心化且没有单点故障的拓扑结构&#xff0c;稳定的消息传输发布保障&#xff0c;能够具有高容错和高可用特性。 易于扩展&#xf…

星辰秘典:揭开Python项目的神秘密码——2048游戏

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;html css js&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;你好&#x…

深度学习与神经网络

文章目录 引言1. 神经网络1.1 什么是神经网络1.2 神经元1.3 多层神经网络 2. 激活函数2.1 什么是激活函数2.2 激活函数的作用2.3 常用激活函数解析2.4 神经元稀疏 3. 设计神经网络3.1 设计思路3.2 对隐含层的感性认识 4. 深度学习4.1 什么是深度学习4.2 推理和训练4.3 训练的相…

python语法(高阶)-多线程编程

""" 演示多线程编程的使用 """ import time import threadingdef sing(msg):while True:print(msg)time.sleep(1)return Nonedef dance(msg):while True:print(msg)time.sleep(1)return Noneif __name__ __main__:# 创建一个唱歌的线程&#xf…

html实现好看的多种风格导航菜单(附源码)

文章目录 1.设计来源1.1 顶部导航菜单1.1.1 界面风格1-一二级连体导航菜单1.1.2 界面风格2-二级导航下拉框1.1.3 界面风格3-系统开始风格1.1.4 界面风格4-购物类导航菜单1.1.5 界面风格5 - 带搜索扩展的导航条1.1.6 界面风格6-火热效果多级导航条 1.2 悬浮按钮菜单1.2.1 界面风…

电力系统系统潮流分析【IEEE 57 节点】(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

Ceph分布式存储系统搭建

目录 安装部署示例 &#xff08;一&#xff09;准备环境 1). 设置主机名 2). 关闭防火墙 3).添加sdb磁盘并格式化 4).配置hosts解析文件 5).配置免密登录 6).同步时区 7). 安装 Ceph 包 &#xff08;二&#xff09;创建 Ceph 集群 1、 安装ceph-deploy管理工具 2、 …

Linux 用户名称高亮和最近路径显示

1、通常情况下&#xff0c;Linux中的路径名称会不断叠加显示&#xff0c;如下图&#xff0c;这样看起来会很长。 2、为了设置路径只是当前最近的文件路径&#xff0c;先进入自己的家目录&#xff0c;然后进入.bashrc&#xff1a; 3、在.bashrc文件中的最后一行加入以下内容…

C国演义 [第三章]

第三章 组合分析步骤递归函数的返回值和参数递归结束的条件单层逻辑 组合总和 III 组合 力扣链接 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1…

Echarts区域面积areaStyle用图片进行纹理填充

React DOM结构代码&#xff1a; import fillImg from xx/fillImg.png; // 填充纹理图片...... {/* 趋势图填充纹理图片 */} <img id"fillImg" src{fillImg} style{{ width: 0 }} /> <div id"line" style{{ width: 100%, height: 300 }}></…

蓝绿发布、灰度发布和滚动发布

系列文章目录 文章目录 系列文章目录一、1.金丝雀发布&#xff08;Canary Release&#xff09;的工作原理&#xff1a;2.滚动发布&#xff08;Rolling Release&#xff09;3.蓝绿发布&#xff08;Blue-Green Deployment&#xff09;有钱人玩的&#xff01; 总结 一、 当涉及到…

深入理解深度学习——注意力机制(Attention Mechanism):带掩码的多头注意力(Masked Multi-head Attention)

分类目录&#xff1a;《深入理解深度学习》总目录 相关文章&#xff1a; 注意力机制&#xff08;AttentionMechanism&#xff09;&#xff1a;基础知识 注意力机制&#xff08;AttentionMechanism&#xff09;&#xff1a;注意力汇聚与Nadaraya-Watson核回归 注意力机制&#…

WPF中的Behavior及Behavior在MVVM模式下的应用

WPF中的Behavior及Behavior在MVVM模式下的应用 在WPF中&#xff0c;Behaviors&#xff08;行为&#xff09;是一种可重用的组件&#xff0c;可以附加到任何UI元素上&#xff0c;以添加特定的交互行为或功能。Behaviors可以通过附加属性或附加行为的方式来实现。 Behavior并不…