目录
std::atomic续、std::async与std::thread对比
std::atomic续、std::async与std::thread对比
原子操作std::atomic续谈
#include <iostream> #include <string> #include <thread> #include <mutex> #include <future> std::atomic<int> my_count{ 0 }; //封装了一个int类型的对象(值),可以像操作一个int类型的变量一样操作my_count void my_thread() //线程入口函数 { for (int i = 0; i < 1000000; i++) { // my_count++; //对应的是一个原子操作,不会被打断 my_count += 1; } return; } int main() { std::thread threadObj1(my_thread); std::thread threadObj2(my_thread); threadObj1.join(); threadObj2.join(); std::cout << "两个线程执行完毕,最终 my_count的值是" << my_count << std::endl; return 0; }
- 上述my_count++ ,及my_count += 1;都能保证是原子操作,结果正确,如果改为my_count = my_count + 1;则结果不正确:
- 一般atomic原子操作,针对++,--,+=,&=,|=,^=是支持的。其他的可能不支持。
std::async与std::thread对比
std::async参数详述 ,async用来创建 一个异步任务
#include <iostream> #include <string> #include <thread> #include <mutex> #include <future> using namespace std; int mythread() //线程入口函数 { cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl; //打印线程id return 100; } int main() { cout << "MianThreadID = " << std::this_thread::get_id() << endl; //打印主线程id std::future<int> ret = std::async(mythread); //创建线程并开始执行 //将future对象与async创建的线程绑定到一起,流程不卡在这里,继续运行 std::cout << ret.get() << std::endl; std::cout << "主线程结束" << std::endl; return 0; }
- 参数 std::launch::deferred【延迟调用】 ,以及std::launch::async【强制创建一个线程】
- 使用std::thread() 创建线程,如果系统资源紧张,那么可能创建线程就会失败,那么执行std::thread()时整个程序可能崩溃。
- std::async()我们一般不叫创建线程(解释async能够创建线程),我们一般叫它创建 一个异步任务。
- std::async和std::thread最明显的不同,就是async有时候并不创建新线程。
- a)如果你用std::launch::deferred来调用async会怎么样?
- std::launch::deferred延迟调用,并且不创建新线程,延迟到future对象调用 get()或者 wait() 的时候才执行mythread()。
- 如果没有调用get或者wait,那么这个mythread()不会执行。
- b)std::launch::async:强制这个异步任务在新线程上执行,这 意味着,系统必须要给我创建出新线程来运行mythread()。
- c)std::launch::async | std::launch::deferred,这里这个 | :意味着调用async的行为可能是 “ 创建新线程并立即执行” 或者 没有创建新线程并且延迟到调用 result.get()才开始执行任务入口函数,两者居其一。
- d)不带额外参数,只给async函数一个 入口函数名:
- 默认值应该是std::launch::async | std::launch::deferred;和c)效果完全一致。
- 换句话说:系统会自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行。
- 自行决定是啥意思?系统如何决定是 异步(创建新线程)还是同步(不创建新线程)方式运行 ?
std::async和std::thread的区别(重点)
【std::thread】
- std::thread创建线程,如果系统资源紧张,创建线程失败,那么整个程序就会报异常崩溃(有脾气)。
- std::thread创建线程的方式,如果线程返回值,你想拿到这个值也不容易。
【std::async】
- std::async创建异步任务。可能创建也可能不创建线程。
- std::async调用方法很容易拿到线程入口函数的返回值。
【系统资源限制的影响】
- (1)如果用std::thread创建的线程太多,则可能创建失败,系统报告异常,崩溃。
- (2)如果用std::async,一般就不会报异常不会崩溃,因为 如果系统资源紧张导致无法创建新线程的时候,std::async这种不加额外参数的调用 就不会创建新线程。而是后续谁调用了result.get()来请求结果,那么这个异步任务mythread就运行在执行这条get()语句所在的线程上。
- 如果你强制std::async一定 要创建新线程,那么就必须使用 std::launch::async。承受的代价就是系统资源紧张时,程序崩溃。
- (3)经验:一个程序里,线程数量不宜超过100-200,原因是时间片的切换。
std::async不确定性问题的解决
- 不加额外参数的std::async调用 ,让系统自行决定是否创建新线程。
- 问题焦点在于 std::future<int> result = std::async(mythread); 这种写法写法:
- 这个异步任务到底有没有被推迟执行,(即系统选择的是std::launch::async还是std::launch::deferred方式)。
- std::future对象的wait_for函数(注意等待 0 秒进行判断系统采用的策略):
std::future<int> result = std::async(mythread); //想判断async到底有没有创建新线程立即执行还是延迟(没创建新线程)执行 std::future_status status = result.wait_for(0s); //(std::chrono::seconds(0)); if (status == std::future_status::deferred) { //线程被延迟执行了(系统资源紧张了,它给我采用std::launch::deferred策略了) cout << result.get() << endl; //这个时侯才去调用了mythread(); } else { //任务没有被推迟,已经开始运行了被,线程被创建了; if (status == std::future_status::ready) { //线程成功返回 cout << "线程成功执行完毕并返回!" << endl; cout << result.get() << endl; } else if (status == std::future_status::timeout) { //超时线程还没执行完 cout << "超时线程没执行完!" << endl; cout << result.get() << endl; } }
总结
- 使用async,可能两种情况:std::launch::async或者std::launch::deferred,可以通过 future的状态来进行判断,利用std::future对象的wait_for函数等待0秒(相当于执行到这里就立即得到future对象的状态)。
- 如果系统采用的是std::launch::deferred方式,那么std::future对象就是std::launch::deferred状态。
- 如果系统采用的是std::launch::async方式,那么std::future对象有可能有两种状态,因为在新线程可能执行完也有可能没有执行完,此时需要判断std::future对象状态是std::future_status::ready还是std::future_status::timeout。