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();
}