C++并发与多线程编程--async、future、packaged_task、promise、atomic


1、std::async、std::future创建后台任务并返回

std::async是个函数模板,用于启动一个异步任务,启动起来一个异步任务后,返回一个std::future对象,std::future是个类模板

启动异步任务,即自动创建一个线程并开始执行对应的线程入口函数,它可以返回一个std::future对象。

std::future对象含有线程入口函数所返回的结果(线程返回的结果),我们可以通过调用future对象的成员函数**get()**获取。提供了一种访问异步操作的机制。

int mythread1(int param)
{
    cout << "threadid="<<std::this_thread::get_id() << endl;
    std::chrono::milliseconds dura(5000); 	//5秒
    std::this_thread::sleep_for(dura);		//sleep5秒
    return 5;
}

class A
{
public: 
    int mythread2(int param)
    {
        cout << "threadid="<<std::this_thread::get_id() << endl;
        std::chrono::milliseconds dura(5000); 	//5秒
        std::this_thread::sleep_for(dura);		//sleep5秒
        return 6;
    }
};

int main()
{
    A a;
    int tempar = 12;
    cout << "mainid = " << std::this_thread::get_id()<< endl;
    //创建一个线程并开始执行,绑定关系;流程并不会卡在这里
    //std::future<int> result = std::async(mythread1, tempar); 
    
	//第2个参数是对象引用,才能保证线程里用的是同一个对象
    std::future<int> result = std::async(&A::mythread2, &a, tempar);    
    cout << "continue..." <<endl;
    int def = 0;
    cout << result.get()<< endl;  	//6 等待mythread执行完,拿到结果(只能调用一次,不能调用多次)
    //result.wait();                //等待线程返回,本身并不返回结果
    cout << "I love China"<< endl;
}

std::async()传递参数,该参数类型是std::launch(枚举类型)
(1)std::launch::deferred表示线程入口函数被延迟到std::future的wait()或者get()函数调用时才执行;

std::future<int> result = std::async(std::launch::deferred,&A::mythread2, &a, tempar);   

如果在此之后不调用wait()或者get(),那么上述线程不会被执行。
如果在之后调用get()函数,并没有创建新线程,是在主线程中调用的线程入口函数。


(2)std::launch::async 不需要get(),新线程也会创建并执行

std::future<int> result = std::async(std::launch::async,&A::mythread2, &a, tempar);  


(3)当不指定参数时,默认参数为:std::launch::deferred | std::launch::async,系统会自行选择一种模式。


2、 future成员函数

#include <iostream>
#include <thread>
#include <future>

using namespace std;

int mythread(void)
{
	cout << "threadid=" << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000); 	//5秒
	std::this_thread::sleep_for(dura);		//sleep5秒
	return 5;
}
int main()
{
	std::future<int> result = std::async(mythread);
	
	//等待1s 即使线程mythread耗时5s,1s钟后这里不再等待
	std::future_status status = result.wait_for(std::chrono::seconds(1)); 
	if (status == std::future_status::timeout)
	{
		//线程还未执行完毕
		cout << "timeout1" << endl;
	}
	else if (status == std::future_status::ready)
	{
		//线程成功执行完毕
		cout << "timeout2" << endl;
	}
	else if (status == std::future_status::deferred) //延迟
	{
		//如果async的第一个参数被设置为std::launch::deferred,则本条件成立
		cout << "timeout3" << endl;
	}

    std::cout << "Hello World!\n";
	getchar();
}

运行结果:
在这里插入图片描述

3、shared_future

future的get()函数为转移数据,只能get一次;shared_future的get()函数为复制数据,可以get多次。

#include <iostream>
#include <thread>
#include <future>

using namespace std;

int mythread(int mypar)
{
	cout << "threadid=" << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000); 	//5秒
	std::this_thread::sleep_for(dura);		//sleep5秒
	return 5;
}

void mythread2(std::future<int> &tmpf)
{
	auto result = tmpf.get(); //获取数值(转移数值),只能获取一次,否则异常
	return;
}

int main()
{
	std::packaged_task<int(int)> mypt(mythread);
 
 	//线程开始执行, 第二个参数作为线程入口函数的参数
	std::thread t1(std::ref(mypt), 1);
	t1.join();

	std::future<int> result = mypt.get_future();
	//std::thread t2(mythread2, std::ref(result)); 修改前,直接使用future的result,而只能获取一次
	//t2.join();
	//修改后,使用shared_future的result_s可以get多次
	std::shared_future<int> result_s(std::move(result)); //执行后result为空。语句执行效果等同于下句:
			
	//std::shared_future<int> result_s(mypt.get_future()); 
	auto mythreadresult = result_s.get();	//mythreadresult=5
	mythreadresult = result_s.get();		//mythreadresult=5

	/*std::thread t2(mythread2, std::ref(result_s));
	t2.join();*/

    //std::cout << "Hello World!\n";
	getchar();
}


4、std::packaged_task打包任务

类模板,模板参数是各种可调用对象;把各种可调用对象包起来,方便作为线程入口函数。
包装起来的可调用对象还可以直接被调用,packaged_task是可调用对象。

int main()
{
    std::packaged_task<int (int)> mypt(mythread1); 	//把函数mythread1包装起来
    std::thread t1(std::ref(mypt), 1);    			//线程直接开始执行, 第二个参数作为线程入口函数的参数
    t1.join();                                      //等待线程执行完成

    std::future<int> result = mypt.get_future(); 	//std::future对象包含线程入口函数的返回结果
    cout << result.get() << endl;
}

//lambda
std::packaged_task<int (int)> mypt([](int myparam){
cout << “threadid=”<<std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000); //5秒
std::this_thread::sleep_for(dura);//sleep5秒
return 5;
});

//可以直接调用
mypt(150);
std::future result=mypt.get_future();
cout << result.get() << endl;

//容器操作
vector<std::packaged_task<int(int)>> mytasks;
mytasks.push_back(std::move(mypt)); //入容器,使用移动语义,入进去之后mypt就为空
//…
std::packaged_task<int(int)> mypt2;
auto iter = mytasks.begin();
mypt2 = std::move(*iter); //移动语义
mytasks.erase(iter); //删除第一个元素,迭代就已经失效,后续代码不可再使用iter
mypt2(123);
std::future result = mypt2.get_future();
cout << result.get() << endl;


5、std::promise

在某个线程中赋值,在另外一个线程中可以取出结果。
通过promise保存一个值,在将来某个时刻我们通过把一个future绑定到这个promise上得到这个绑定的值。

void mythread(std::promise<int> &tmp, int cals)
{
    //........
    cals += 10;
    int result = cals;
    tmp.set_value(result);
}

void mythread2(std::future<int> &tmpf)
{
    auto result = tmpf.gte();
    cout << result <<endl;
    return;
}


//------------------------调用---------------------//
std::promise<int> myprom;
std::thread t1(mythread, std::ref(myprom), 180);
t1.join();

//获取结果值
std::future<int> ful = myprom.get_future();    //promise和future绑定,用于获取线程返回值
//auto result = ful.get();                     //get只能调用一次
//cout << result << endl;
//第二个线程拿来用
std::thread t2(mythread2, std::ref(ful));
t2.join();


6、atomic原子操作

原子操作概念引出范例:
互斥量:多线程编程中,保护共享数据:锁,操作共享数据:解锁。
有两个线程,对一个变量进行操作,这个线程读取变量,另一个线程往这个变量写值。

可以把原子操作理解成一种不需要用到互斥量加锁(无锁)技术的多线程并发编程。
原子操作:在多线程中不会被打断的程序执行片段;效率上比互斥量高。

与互斥量相比不足的是,互斥量可以针对多行代码加锁;原子操作是对一个变量操作,不能对多行代码操作。
原子操作一般不可分割,要么是已经执行完毕或者没执行,不可能是半完成状态。

int mycount = 0;
std::mutex g_my_mutex;
std::atomic<int> g_mycount = 0;

void mythread1()
{
	for(int i = 0; i < 10000000; i++)
	{
		mycount++;
	}
}

void mythread2()
{
	for(int i = 0; i < 10000000; i++)
	{
		g_my_mutex.lock();
		mycount++;
		g_my_mutex.unlock();
	}
}

void mythread3()
{
	for(int i = 0; i < 10000000; i++)
	{
		g_mycount++;
	}
}


int main()
{
	
	//(1)此种方法,mycount无法达到预期的总和20000000
	std::thread t1(mythread1);
	std::thread t2(mythread1);
	t1.join();
	t2.join();
	
	//(2)此种方法,mycount可以达到预期的总和20000000,但耗时较长
	std::thread t3(mythread2);
	std::thread t4(mythread2);
	t3.join();
	t4.join();
	
	//(3)此种方法,g_mycount为原子操作类型
	std::thread t5(mythread3);
	std::thread t6(mythread3);
	t5.join();
	t6.join();
}

其他用法(标记)

std::atomic<bool> g_ifend = false;

void mythread()
{
	std::chrono::milliseconds dura(1000);
	while(!g_ifend)
	{
		cout << " threadid = "<< std::this_thread::get_id()<< endl;
		std::this_thread::sleep_for(dura);
	}
	cout << " threadid = "<< std::this_thread::get_id()<< endl;
}

int main()
{
	std::thread t1(mythread);
	std::thread t2(mythread);
		
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	
	g_ifend = true;
	t1.join();
	t2.join();
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值