C++并发与多线程(四)

future的其他成员函数、shared_future

std::future的其他成员函数

std::future_status

std::future_status是一个迭代值,里面只有三个状态:timeout、ready、deferred。使用如下程序:

int mythread()//线程入口函数
{
	cout << "mythread() start" << "thread id = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);//休息5s
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "thread id = " << std::this_thread::get_id() << endl;
	return 5;
}



int main()
{
	cout << "main" << "thread id = " << std::this_thread::get_id() << endl;
	std::future<int> result = std::async(std::launch::deferred,mythread);
	cout << "continue..........!" << endl;
	/*cout << result.get() << endl;*/
	std::future_status status = result.wait_for(std::chrono::seconds(6));//等待1s,希望得到返回值
	if (status == std::future_status::timeout)
	{
	
		//超时表示线程还没有执行完;
		cout << "超时,线程没有执行完毕!" << endl;
	}
	else if (status == std::future_status::ready)
	{
		//ready表示线程成功返回
		cout << "成功,线程执行完毕!" << endl;

	}
	else if (status == std::future_status::deferred)//deferred表示延迟的意思
	{
		//如果async的第一个参数被设为std::launch::deferred且后面调用get()就成立
		cout << "线程被延迟执行" << endl;
		cout << result.get() << endl;
	}
	cout << "主线程执行结束" << endl;
	return 0;

}

wait_for()返回三个枚举值。

std::shared_future

std::shared_future也是一个类模板。std::future的get() 转移数据,而std::shared_future的get() 复制数据。

int mythread(int mypar)//注意第一个参数
{
	cout << "mythread start " << "thread id = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);//休息5s
	std::this_thread::sleep_for(dura);
	return 5;//保存结果到tmpp这个对象中。

}
void mythread2(std::shared_future<int>& tmpf)
{
	cout << "mythread2 start " << "thread id = " << std::this_thread::get_id() << endl;
	auto result = tmpf.get();
	cout << "mythread2 result = " << result << endl;
	return;
}
int main()
{

	cout << "main" << " thread id = " << std::this_thread::get_id() << endl;

	std::packaged_task<int(int)> mypt(mythread);
	std::thread t1(std::ref(mypt), 1);
	t1.join();
	//1)第一种写法
	//std::future<int> result = mypt.get_future();
	//std::shared_future<int> result_s(std::move(result));//执行后 result对象空了,reslut_s有值
	//auto mythreadresult = result_s.get();
	//mythreadresult = result_s.get();
	//或者
	//std::shared_future<int> result_s(result.share());
	//2)第二种写法
	std::shared_future<int> result_s(mypt.get_future());
	std::thread t2(mythread2, std::ref(result_s));
	t2.join();
	cout << "主线程执行完毕!" << endl;
	return 0;
}

原子操作std::atomic

原子操作概念引出范例

互斥量:多线程编程中保护共享数据----锁,操作共享数据,开锁。

有两个线程,对一个变量进行操作,这个线程读,另外一个线程往变量写值。可能会出现像是数据库操作中的幻影读等问题。于是引入了原子操作。

int g_mycout = 0;
void mythread()
{
	for (int i = 0; i < 1000000; i++)
	{
		g_mycout++;
	}
	return;
}
int main()
{
	std::thread mytobj1(mythread);
	std::thread mytobj2(mythread);

	mytobj1.join();
	mytobj2.join();

	cout << "两个线程执行完毕,最终g_mycount结果是:" << g_mycout << endl;

	cout << "主线程执行完毕!" << endl;
	return 0;
}

结果:

两个线程执行完毕,最终g_mycount结果是:1017600
主线程执行完毕!

结果不对,事实上每次结果都可能不一样,两个线程同时加一个变量是不安全的。

原因是因为在汇编中,一行代码被分解为几行汇编代码,出现问题。

使用互斥量来解决:

std::mutex g_my_mutex;
void mythread()
{
	for (int i = 0; i < 1000000; i++)
	{
		g_my_mutex.lock();
		g_mycout++;
		g_my_mutex.unlock();
	}
	return;
}

结果不会出现错误,但是牺牲了效率。这里就引入原子操作。

可以把原子操作理解成不需要用到互斥量加锁的多线程并发编程技术,或者是在多线程中不会被打断的程序执行片段。

互斥量的的加锁一般是针对一个代码段(几行代码),而原子操作针对的一般都是一个变量。

原子操作,一般都是指”不可分割的操作”,要么是完成状态,要么是未完成状态,不可能是半完成状态。

基本的std::atomic用法范例

std::atomic代表原子操作,十个类模板,用来封装某个类型值的。

std::atomic<int> g_mycout = 0;//封装了一个类型为int的对象(值);我们可以像操作一个int类型变量一样来操作g_mycout
void mythread()
{
	for (int i = 0; i < 1000000; i++)
	{	
		g_mycout++;//atomic对象,对应的操作是原子操作(不会被打断)
	}
	return;
}
int main()
{
	std::thread mytobj1(mythread);
	std::thread mytobj2(mythread);

	mytobj1.join();
	mytobj2.join();

	cout << "两个线程执行完毕,最终g_mycount结果是:" << g_mycout << endl;

	cout << "主线程执行完毕!" << endl;
	return 0;
}

这样运行结果正确且稳定。

但是如果把 g_mycout++改为 g_mycout = g_mycout + 1; 之后,结果出现错误。

所以,不是所有运算符都符合原子操作。

一般,atomic原子操作针对++、–、+=、&=、|=、^=是支持的,其他的可能不支持。

原子操作bool类型可以控制线程是否结束:

std::atomic<bool> g_ifend = false;//线程退出标记,作为原子操作,防止读写同时完成。
void mythread()
{
	std::chrono::seconds dura(1);//1s
	while (g_ifend == false)
	{
		//系统没有要求线程退出,所以线程可以干事
		cout << "thread id = " << std::this_thread::get_id() << "运行中" << endl;
		std::this_thread::sleep_for(dura);
	}
	cout << "thread id = " << std::this_thread::get_id() << "运行结束" << endl;
	return;
}
int main()
{
	std::thread mytobj1(mythread);
	std::thread mytobj2(mythread);
	std::chrono::seconds dur(5);
	std::this_thread::sleep_for(dur);

	g_ifend = true;

	mytobj1.join();
	mytobj2.join();

	cout << "主线程执行完毕!" << endl;
	return 0;
}

运行结果:

thread id = thread id = 7276运行中
10724运行中
thread id = 7276运行中
thread id = 10724运行中
thread id = 10724运行中
thread id = 7276运行中
thread id = 7276运行中
thread id = 10724运行中
thread id = 7276运行中
thread id = 10724运行中
thread id = 7276运行结束
thread id = 10724运行结束
主线程执行完毕!

心得

原子操作一般用于计数或者统计(累计发出去了多少个数据包,累计接收到了多少个数据包)。

std::async深入

std::async参数详述

async用来创建一个异步任务;之前讲过一个延迟调用的参数叫:std::launch::deferred,以及强制创建线程:std::launch::async

std::thread() 如果系统资源紧张,那么可能创建线程就会失败,那么执行std::thread() 时整个程序可能崩溃。

std::async() 一般不叫创建线程(即使async可以创建线程),我们一般叫它创建一个异步任务。

std::async和std::thread最明显的不同,就是async有时候并不创建新线程。

a)如果用std::launch::deferred来调用async会怎么样?

int mythread()
{
	cout << "mythread() start" << "thread id = " << std::this_thread::get_id() << endl;
	return 1;
}
int main()
{
	cout << "main start" << "thread id = " << std::this_thread::get_id() << endl;
	std::future<int> result = std::async(std::launch::deferred,mythread);
	cout << result.get() << endl;
	return 0;
}

这时没有新线程创建:

main startthread id = 14400
mythread() startthread id = 14400
1

如果不调用get() 的话,mythread都没有执行。

所以deferred延迟调用在遇到get()之前不会执行线程,并且就算执行也不会创建新线程。

b)std::launch::async的话又是什么情况?

main startthread id = 21608
mythread() startthread id = 1400
1

可以从结果里面知道:std::launch::async 强制这个异步任务在新线程上执行,必须给这个任务创建出新线程。

c)std::launch::async | std::launch::deferred 会出现什么情况?

main startthread id = 20852
mythread() startthread id = 15320
1

依然创建出了新线程。

虽然效果和std::launch::async 一样,但是原理不同:

这里这个 | 意味着调用async的行为可能是std::launch::async创建新线程并立即执行,或者是 std::launch::deferred 没有创建新线程延迟到调用result.get()才开始执行任务入口函数。 两者选其中一个。

d)不带额外参数的情况

std::future<int> result = std::async(mythread);

main startthread id = 6844
mythread() startthread id = 22352
1

创建了新线程,其实当没有指定额外参数时,默认值应该是c)情况。系统默认是同步还是异步方式运行。

下面来探寻系统如何决定是异步还是同步执行?

下面开始讨论。

std::async和std::thread 的区别

std::thread() 如果系统资源紧张,那么可能创建线程就会失败,那么执行std::thread() 时整个程序可能崩溃,并且不好接收线程的返回值。

std::async创建异步任务。可能创建也可能不创建线程。并且很容易拿到线程入口函数的返回值。

由于系统资源限制,就出现一种情况:(1)如果用std::thread创建的线程太多,则可能创建失败,系统报告异常。

​ (2)如果用std::async不会出现异常,因为无法创建线程的时候就可以直接同步调用,不会创建新线程。而是设后续调用了get() 来请求结果的时候直接在get() 的线程上调用。如果强势std::async一定要创建新线程,那么必须使用std::launch::async。

​ (3)一个程序里,线程数量不宜超过100-200,时间片问题。

std::async不确定性问题的解决

出现不确定性的问题主要是选择让系统自动选择调用方式。

之前有一个std::future 函数 std::future_status

int mythread()//线程入口函数
{
	cout << "mythread() start" << "thread id = " << std::this_thread::get_id() << endl;
	std::chrono::seconds dura(5);//休息5s
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "thread id = " << std::this_thread::get_id() << endl;
	return 5;
}



int main()
{
	cout << "main" << "thread id = " << std::this_thread::get_id() << endl;
	std::future<int> result = std::async(mythread);//判断async到底有没有创建新线程

	/*cout << result.get() << endl;*/
	std::future_status status = result.wait_for(0s);//(std::chrono::seconds(0));//等待1s,希望得到返回值
	if (status == std::future_status::deferred)//deferred表示延迟的意思
	{
		//如果async的第一个参数被设为std::launch::deferred且后面调用get()就成立
		cout << "线程被延迟执行" << endl;
		cout << result.get() << endl;//这个时候才去调用了mythread();
	}
	else
	{
		if (status == std::future_status::timeout)
		{

			//超时表示线程还没有执行完;
			cout << "超时,线程没有执行完毕!" << endl;
			cout << result.get() << endl;
		}
		else if (status == std::future_status::ready)
		{
			//ready表示线程成功返回
			cout << "成功,线程执行完毕!" << endl;
			cout << result.get() << endl;

		}
	}
	cout << "主线程执行结束" << endl;
	return 0;

}

这样可以等待它运行完之后调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

word_no_bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值