C++11中多线程编程-std::async的深入讲解

前言

C++11中提供了异步线程接口std::async,std::async是异步编程的高级封装,相对于直接使用std::thread,std::async的优势在于:

1、std::async会自动创建线程去调用线程函数,相对于低层次的std::thread,使用起来非常方便;

2、std::async返回std::future对象,通过返回的std::future对象我们可以非常方便的获取到线程函数的返回结果;

3、std::async提供了线程的创建策略,可以指定同步或者异步的方式去创建线程;

1、函数原型

C++ 11中提供如下函数原型:

template< class Function, class... Args>
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
 async( Function&& f, Args&&... args );
template< class Function, class... Args >
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
 async( std::launch policy, Function&& f, Args&&... args );

其中,参数f接收一个可调用对象(仿函数、lambda表达式、类成员函数、普通函数……),用于异步或是同步执行。

参数policy用于指定同步执行或者异步执行可调用对象,它的可选值有三种:

1)std::launch::async:异步执行可调用对象;

2)std::launch::deferred:同步执行可调用对象;

3)std::launch::async | std::launch::deferred 可以异步或是同步,取决于具体实现。

函数返回值:

函数返回值是std::future对象,我们可以执行get、wait、wait_for、wait_until函数获取或者等待执行结果。

调用std::future对象的get函数时,如果执行的是异步执行策略,如果异步执行没有结束,get函数调用会阻塞当前当前调用线程;如果执行的是同步执行策略,只有当调用get函数时才真正执行。

调用std::future对象的wait*函数时,可能返回三种状态:

1)std::future_status::deferred:可调用对象尚未开始执行;

2)std::future_status::ready:可调用对象执行完毕;

3)std::future_status::timeout:可调用对象执行超时;

2、头文件

#include <future>

3、同步或异步读取文件内容

我们模拟异步从数据库中读取数据和同步方式从文件中读取数据,从其中可以看到std::async的使用方法。

#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>

using namespace std::chrono;

std::string fetchDataFromDB(std::string recvData) {
 std::cout << "fetchDataFromDB start" << std::this_thread::get_id() << std::endl;
 std::this_thread::sleep_for(seconds(5));
 return "DB_" + recvData;
}

std::string fetchDataFromFile(std::string recvData) {
 std::cout << "fetchDataFromFile start" << std::this_thread::get_id() << std::endl;
 std::this_thread::sleep_for(seconds(3));
 return "File_" + recvData;
}

int main() {
 std::cout << "main start" << std::this_thread::get_id() << std::endl;

 //获取开始时间
 system_clock::time_point start = system_clock::now();

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

 //获取结束时间
 auto end = system_clock::now();

 auto diff = duration_cast<std::chrono::seconds>(end - start).count();
 std::cout << "Total Time taken= " << diff << "Seconds" << std::endl;

 //组装数据
 std::string data = dbData + " :: " + FileData;

 //输出组装的数据
 std::cout << "Data = " << data << std::endl;

 return 0;
}

代码输出:

main start140677737994048
fetchDataFromFile start140677737994048
fetchDataFromDB start140677720131328
Total Time taken= 5Seconds
Data = DB_Data :: File_Data

4、设置异步数据读取超时机制

有时我们不能无限制的等待异步任务执行,可以设置超时等待时间(timeout),当超时时间到达时,可以选择放弃等待异步任务。

如果代码中,我们设置了1s的超时设置,用于检查异步线程是否执行完毕。

#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>

using namespace std::chrono;

std::string fetchDataFromDB(std::string recvData) {

 std::cout << "fetchDataFromDB start" << std::this_thread::get_id() << std::endl;
 std::this_thread::sleep_for(seconds(5));
 return "DB_" + recvData;
}

int main() {

 std::cout << "main start" << std::this_thread::get_id() << std::endl;

 //获取开始时间
 system_clock::time_point start = system_clock::now();

 std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");

 std::future_status status;
 std::string dbData;
 do
 {
  status = resultFromDB.wait_for(std::chrono::seconds(1));

  switch (status)
  {
  case std::future_status::ready:
   std::cout << "Ready..." << std::endl;
   //获取结果
   dbData = resultFromDB.get();
   std::cout << dbData << std::endl;
   break;
  case std::future_status::timeout:
   std::cout << "timeout..." << std::endl;
   break;
  case std::future_status::deferred:
   std::cout << "deferred..." << std::endl;
   break;
  default:
   break;
  }

 } while (status != std::future_status::ready);

 //获取结束时间
 auto end = system_clock::now();

 auto diff = duration_cast<std::chrono::seconds>(end - start).count();
 std::cout << "Total Time taken= " << diff << "Seconds" << std::endl;

 return 0;
}

程序输出:

main start140406593357632
fetchDataFromDB start140406575482624
timeout...
timeout...
timeout...
timeout...
Ready...
DB_Data
Total Time taken= 5Seconds

5、使用std::async实现多线程并发

既然std::async可以实现异步调用,我们很容易就可以借用它实现多线程并发。

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>
#include <string>
#include <mutex>
#include <thread>

template <typename RandomIt>
int parallel_sum(RandomIt beg, RandomIt end)
{
 std::cout << "thread id:" << std::this_thread::get_id() << std::endl;

 auto len = end - beg;
 if (len < 1000)
  return std::accumulate(beg, end, 0);

 RandomIt mid = beg + len/2;
 auto handle_me = std::async(std::launch::async,
        parallel_sum<RandomIt>, mid, end);
 auto handle_bm = std::async(std::launch::async,
        parallel_sum<RandomIt>, beg, mid);
 // int sum = parallel_sum(beg, mid);
 return handle_bm.get() + handle_me.get();
}

int main()
{
 std::vector<int> v(10000, 1);
 std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << std::endl;
}

程序输出如下:

The sum is thread id:140594794530624
thread id:140594776655616
thread id:140594768262912
thread id:140594759870208
thread id:140594672297728
thread id:140594680690432
thread id:140594663905024
thread id:140594655512320
thread id:140594647119616
thread id:140594638726912
thread id:140594269644544
thread id:140594630334208
thread id:140594278037248
thread id:140594252859136
thread id:140594261251840
thread id:140594252859136
thread id:140594236073728
thread id:140594252859136
thread id:140594261251840
thread id:140594630334208
thread id:140594244466432
thread id:140594252859136
thread id:140594227681024
thread id:140594261251840
thread id:140593875384064
thread id:140593850205952
thread id:140593858598656
thread id:140593866991360
thread id:140594647119616
thread id:140594269644544
thread id:140594672297728
10000

6、其它注意事项

在使用时需要注意,std::future对象的析构需要等待std::async执行完毕,也就是说,如下面的代码并不能实现并发。原因在于std::async的返回的std::future对象无人接收,是个临时变量,临时变量的析构会阻塞,直至std::async异步任务执行完成。

std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f()
std::async(std::launch::async, []{ g(); }); // does not start until f() completes

参考材料

https://en.cppreference.com/w/cpp/thread/async

www.jb51.net/article/198761.htm

总结

到此这篇关于C++11中多线程编程-std::async深入讲解的文章就介绍到这了,更多相关C++11多线程编程-std::async内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++多线程中的锁和条件变量使用教程

    在做多线程编程时,有两个场景我们都会遇到: 多线程访问共享资源,需要用到锁: 多线程间的状态同步,这个可用的机制很多,条件变量是广泛使用的一种. 今天我用一个简单的例子来给大家介绍下锁和条件变量的使用. 代码使用C++11 示例代码 #include <iostream> #include <mutex> #include <thread> #include <condition_variable> std::mutex g_mutex; // 用到的全局锁

  • c++11多线程编程之std::async的介绍与实例

    本节讨论下在C++11中怎样使用std::async来执行异步task. C++11中引入了std::async 什么是std::async std::async()是一个接受回调(函数或函数对象)作为参数的函数模板,并有可能异步执行它们. template<class Fn, class... Args> future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn

  • C++多线程获取返回值方法详解

    在许多时候,我们会有这样的需求--即我们想要得到线程返回的值.但是在C++11 多线程中我们注意到,std::thread对象会忽略顶层函数的返回值. 那问题来了,我们要怎么获得线程的返回值呢? 我们通过一个例子来说明如何实现这个需求.用多个线程计算(a+b)/ (x+y) 的值 有两种方法,分别是 1. 传统的方法:在线程间共享指针 #include<iostream> #include<thread> #include<mutex> #include<atom

  • C++多线程编程简单实例

    C++本身并没有提供任何多线程机制,但是在windows下,我们可以调用SDK win32 api来编写多线程的程序,下面就此简单的讲一下: 创建线程的函数 复制代码 代码如下: HANDLE CreateThread(     LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD     SIZE_T dwStackSize,                       // initial stack size     LPTHREAD_START_

  • C++多线程实现电子词典

    本文实例为大家分享了C++多线程实现电子词典的具体代码,供大家参考,具体内容如下 // Dictionary.cpp : 定义控制台应用程序的入口点. //vs2013编译 //字典文件:https://pan.baidu.com/s/1YHtwptaq_V8j034U9_J96A #include "stdafx.h" #include <string> #include <vector> #include <iostream> #include

  • c++11新特性多线程操作实战

    c++11多线程操作 线程 thread int main() { thread t1(Test1); t1.join(); thread t2(Test2); t2.join(); thread t3 = t1; thread t4(t1); thread t5 = std::move(t1); thread t6(std::move(t1)); return 0; } t3,t4创建失败,因为thread的拷贝构造和赋值运算符重载的原型是: thread(const thread&) = d

  • C++11并发编程:多线程std::thread

    一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植,对应多线程代码也必须要修改.现在在C++11中只需使用语言层面的thread可以解决这个问题. 所需头文件<thread> 二:构造函数 1.默认构造函数 thread() noexcept 一个空的std::thread执行对象 2.初始化构造函数 template<class Fn, class... Args> explicit th

  • linux下的C\C++多进程多线程编程实例详解

    linux下的C\C++多进程多线程编程实例详解 1.多进程编程 #include <stdlib.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t child_pid; /* 创建一个子进程 */ child_pid = fork(); if(child_pid == 0) { printf("child pid\n"); exit(0); } else { print

  • c++11 多线程编程——如何实现线程安全队列

    线程安全队列的接口文件如下: #include <memory> template<typename T> class threadsafe_queue { public: threadsafe_queue(); threadsafe_queue(const threadsafe_queue&); threadsafe_queue& operator=(const threadsafe_queue&) = delete; void push(T new_va

  • C++基于消息队列的多线程实现示例代码

    前言 实现消息队列的关键因素是考量不同线程访问消息队列的同步问题.本实现涉及到几个知识点 std::lock_guard 介绍 std::lock_gurad 是 C++11 中定义的模板类.定义如下: template <class Mutex> class lock_guard; lock_guard 对象通常用于管理某个锁(Lock)对象,因此与 Mutex RAII 相关,方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态:而 l

  • c++多线程之死锁的发生的情况解析(包含两个归纳,6个示例)

    一.死锁会在什么情况发生 1.假设有如下代码 mutex; //代表一个全局互斥对象 void A() { mutex.lock(); //这里操作共享数据 B(); //这里调用B方法 mutex.unlock(); return; } void B() { mutex.lock(); //这里操作共享数据 mutex.unlock(); return; } 此时会由于在A.B方法中相互等待unlock而导致死锁. 2.假设有如何代码 mutex; //代表一个全局互斥对象 void A()

  • Windows下使用Dev-C++开发基于pthread.h的多线程程序实例

    一.下载Windows版本的pthread 目前最新版本是:pthreads-w32-2-9-1-release.zip. 二.解压pthread到指定目录 我选择的目录是:E:\DEV-CPP\Pthread 完成后,该目录会多出三个文件夹:Pre-built.2,pthreads.2,QueueUserAPCEx. 三.配置Dev-C++编译选项 1)点击"工具"→"编译选项"→"目录"→"c++包含文件",浏览到刚才解压

随机推荐