作者:小 琛
欢迎转载,请标明出处

参考文章:
傻月菇凉博主-博客

OceanStar的学习笔记博主-博客

NGC_2070博主-博客

文章目录

  • std::thread
    • thread的提出
    • 使用方法、语法
  • join、detach
    • 线程安全问题
      • mutex的提出
      • 最优的使用方法:std::lock_guard
  • std::async
    • std::async的提出
    • 使用方法、语法
  • 二者的使用区别

std::thread

C++官方链接

thread的提出

C++11提出了线程相关的内容,我们可以直接用它来实现线程操作

看一个例子:

#include <iostream>
#include <thread>void test()
{std::cout << "hello world!" << std::endl;
}int main()
{//hello函数会在新的线程中执行std::thread t(test);//join会在调用线程等待std::thread对象相关联的线程结束 t.join();system("pause"); //msvcreturn 0;
}

使用方法、语法

函数 类别 作用
thread() noexcept 默认构造函数 创建一个线程
template <class Fn, class… Args>explicit thread(Fn&& fn, Args&&… args) 初始化构造函数 创建一个线程,以args为参数
~thread() 析构函数 析构对象

一般来说,我们使用thread去创建一个线程,直接调用接口即可,再将希望执行的异步函数传入即可;而对于执行异步函数的参数,可以直接在创建时传入。注意,一定要保证传入的参数和执行异步函数的参数对应,否则会编译错误
例如下面的例子:

// Compiler: MSVC 19.29.30038.1
// C++ Standard: C++17
#include <iostream>
#include <thread>
using namespace std;
void countnumber(int id, unsigned int n) {for (unsigned int i = 1; i <= n; i++);cout << "Thread " << id << " finished!" << endl;
}
int main() {thread th[10];for (int i = 0; i < 10; i++)th[i] = thread(countnumber, i, 100000000);for (int i = 0; i < 10; i++)th[i].join();return 0;
}

传入的函数支持函数指针、lambda表达式

join、detach

但我们执行下面的代码,会发生编译错误:

#include<iostream>
#include<thread>using namespace std;void thread1() {for(int i=0;i<20;++i)cout << "thread1..." << endl;
}void thread2() {for (int i = 0; i<20; ++i)cout << "thread2..." << endl;
}int main(int argc, char* argv[]) {thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行thread th2(thread2);cout << "main..." << endl;return 0;
}

原因:main函数在创建线程后,继续执行,并retrun 0即结束,但异步的线程并没有结束而引发异常。

熟悉线程相关知识的同学都知道,在创建线程后,要对它“负责到底”

  • thread::join():让主线程等待直到该子线程执行结束。注意,这个接口调用后,线程属性就从joinable变为join,同时已经执行结束的线程是不能被joinable的,所以规范的操作应当是,调用接口前先调用joinable()接口来判断是否可以执行join
  • thread::detach:将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。一旦线程执行完毕,它所分配的资源将会被释放。一定要注意,分离后,有可能主线程执行结束并已经将相关资源释放,而子线程仍在继续执行,因此如果你的程序不能保证子线程一定在主线程执行后结束,请慎用

线程安全问题

mutex的提出

涉及线程,线程安全是一定提出的话题,关于线程安全不在该文章赘述。C++11提出了mutex相关内容,使用要包含头文件《mutex》

举个例子:

#include<iostream>
#include<thread>
#include<mutex>using namespace std;mutex m;
int cnt = 10;void thread1() {while (cnt > 5){m.lock();if (cnt > 0) {--cnt;cout << cnt << endl;}m.unlock();}
}void thread2() {while (cnt > 0) {m.lock();if (cnt > 0) {cnt -= 10;cout << cnt << endl;}m.unlock();}
}int main(int argc, char* argv[]) {thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行thread th2(thread2);th1.join();th2.join();cout << "main..." << endl;return 0;
}

最优的使用方法:std::lock_guard

提起线程会引发线程安全,而提出锁则一定引发死锁相关内容,为了避免该问题发生,C++提出了std::lock_guard,利用RAII思想来规避

使用方法很简单,在需要上锁的地方,将声明的mutex锁交给std::lock_guard管理,它的构造会自动帮助你上锁,而在代码执行完后即对象析构时,会自动解锁,避免了死锁的出现,在实际开发中,应多用这种方式。

看个例子:

#include<iostream>
#include<thread>
#include<mutex>using namespace std;mutex m;
int cnt = 10;void thread1() {while (cnt > 5){lock_guard<mutex> lockGuard(m);if (cnt > 0) {--cnt;cout << cnt << endl;}}
}void thread2() {while (cnt > 0) {lock_guard<mutex> lockGuard(m);if (cnt > 0) {cnt -= 10;cout << cnt << endl;}}
}int main(int argc, char* argv[]) {thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行thread th2(thread2);th1.join();th2.join();cout << "main..." << endl;return 0;
}

std::async

std::async的提出

主要为了解决thread相关的以下几点:

std::async可以直接拿到线程执行结果;
std::async可以避免线程创建失败的情况;
std::async可以手动触发;

使用方法、语法

原型:

template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);

std::async中的第一个参数是启动策略,它控制std::async的异步行为,我们可以用三种不同的启动策略来创建std::async

  • ·std::launch::async
    保证异步行为,即传递函数将在单独的线程中执行
  • std::launch::deferred
    当其他线程调用get()来访问共享状态时,将调用非异步行为
  • std::launch::async | std::launch::deferred
    默认行为。有了这个启动策略,它可以异步运行或不运行,这取决于系统的负载,但我们无法控制它。

通常情况下,我们使用get配合异步操作,看下面的伪代码

std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");//从文件获取数据std::future<std::string> fileData = std::async(std::launch::deferred, fetchDataFromFile, "Data");//直到调用get函数fetchDataFromFile才开始执行std::string FileData = fileData.get();//如果fetchDataFromDB()执行没有完成,get会一直阻塞当前线程std::string dbData = resultFromDB.get();

区别在于:get()+std::launch::async(异步),调用get时异步开始进行,并且不会影响当前线程;get()+std::launch::deferred,当该异步函数没有执行结束,get会一直阻塞当前线程

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

  • 如果你用std::launch::deferred来调用async会怎么样?
    std::launch::deferred延迟调用,并且不创建新线程,延迟到future对象调用 get()或者 wait()
    的时候才执行mythread()。 如果没有调用get或者wait,那么这个mythread()不会执行。
  • std::launch::async:强制这个异步任务在新线程上执行,这 意味着,系统必须要给我创建出新线程来运行mythread()。
  • std::launch::async | std::launch::deferred,这里这个 | :意味着调用async的行为可能是
    “ 创建新线程并立即执行” 或者 没有创建新线程并且延迟到调用 result.get()才开始执行任务入口函数,两者居其一。
  • 不带额外参数,只给async函数一个 入口函数名: 默认值应该是std::launch::async |
    std::launch::deferred;和c)效果完全一致。
    换句话说:系统会自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行。

二者的使用区别

std::thread创建线程,如果系统资源紧张,创建线程失败,那么整个程序就会报异常崩溃(有脾气)。
std::thread创建线程的方式,如果线程返回值,你想拿到这个值也不容易

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

C++——std::async和std::thread相关推荐

  1. C++——异步操作(std::future、std::async、std::packaged_task、std::promise)

    文章目录 一.std::future与std::async 1.基本概念 2.代码演示 二.std::future与std::packaged_task 1.基本知识 2.代码演示 三.std::fu ...

  2. C++多线程std::async、std::future、std::packaged_task、std::promise

    std::async std::async用于创建异步任务,实际上就是创建一个线程执行相应任务,默认立即开始执行. std::async就是异步编程的高级封装,std::async的操作,其实相当于封 ...

  3. C++多线程:异步操作std::async和std::promise

    文章目录 std::async 简介 使用案例 std::promise 简介 成员函数 总结 之前的文章中提到了C++多线程中的异步操作机制 C++ 多线程:future 异步访问类(线程之间安全便 ...

  4. std::future、std::promise、std::packaged_task、std::async

    #include <iostream> #include <string> #include <future> #include <thread> #i ...

  5. std::atomic、std::async深入研究

    1.从上节课的一个demo说起. #include <iostream> #include <future> #include <vector> #include ...

  6. std::future和std::promise和std::packaged_task

    std::future 其实future有两个兄弟,一个是std::future, 一个是它大哥std::shared_future.他们的区别就是std::future只支持移动语义,它所引用的共享 ...

  7. C++ 的 std::promise 和 std::future

    介绍 编写多线程应用时,无法避免的要进行线程间数据同步/通信.std::promise 和 std::future 是 C++ 进行单向数据传递的一种方式.std::promise 是数据的输入端,s ...

  8. C++11中std::async的使用

    C++11中的std::async是个模板函数.std::async异步调用函数,在某个时候以Args作为参数(可变长参数)调用Fn,无需等待Fn执行完成就可返回,返回结果是个std::future对 ...

  9. C++并发编程之std::async(), std::future, std::promise, std::packaged_task

    c++11中增加了线程,使得我们可以非常方便的创建线程,它的基本用法是这样的: void f(int n); std::thread t(f, n + 1); t.join(); 但是线程毕竟是属于比 ...

最新文章

  1. strace,ltrace linux下跟踪进程调用的命令
  2. 什么是静态路由,其特点是什么?什么是动态路由,其特点是什么?
  3. python打印星星居中_python中怎么打印星星
  4. MySQL 8.0.22执行器源码分析HashJoin —— 一些初始化函数的细节步骤
  5. JavaScript学习笔记:对象
  6. linux 新增文件夹记录_linux文件系统(基础概念)
  7. python二分查找例题_Python查找数组中数值和下标相等的元素示例【二分查找】
  8. 淮阴工学院计算机硕士生导师,硕士生导师概况
  9. 126 删除邮件 服务器,网易126邮箱中的邮件莫名奇妙被删除
  10. 汽车距离报警系统c语言编程,基于单片机的汽车防盗报警系统的设计本科生毕业论文.doc...
  11. Bootstrap 导航栏
  12. 用VS软件开发“中国象棋“游戏
  13. 案例分享:Qt激光加工焊接设备信息化软件研发(西门子PLC,mysql数据库,用户权限控制,界面设计,参数定制,播放器,二维图,期限控制,参数调试等)
  14. 下一代云原生应用交付会怎样发展?KubeVela帮大忙。
  15. wireshark https 抓包
  16. 从程序员到项目经理(21):谁都需要成就感
  17. 如何写SCI文章-转自知乎
  18. 计算机与了解Dos指令
  19. 前端 · 深入理解 transform 函数的计算原理 ②
  20. tools自动登录签到脚本

热门文章

  1. HTML标签属性大全(开发人员必备)
  2. 计算机二级ppt操作题题库及答案,计算机二级考试MSOffice考试题库ppt操作题附答案.doc...
  3. 万得终端 linux安装,Linux下常用工具
  4. [网络安全自学篇] 六十七.WannaCry勒索病毒复现及分析(一)Python利用永恒之蓝及Win7勒索加密
  5. 中断响应时间/中断步骤/中断源
  6. 罗森伯格亮相2012北京通讯展
  7. 支付宝,微信个人免签平台搭建
  8. 关于CLAN的一些总结
  9. (附源码)spring boot社区养老医疗服务平台 毕业设计041148
  10. 圆锥曲线条件4a3+27b2 !=0