std::async, std::future, std::launch, std::promise

本文探讨了如何利用std::async和future在C++中实现并发执行,提升效率。通过async创建线程异步执行fetchData_fromDb函数,并通过future获取结果,减少总执行时间。讲解了future的生命周期、状态管理和获取线程返回值的方法,如set_value和get_future的使用。
摘要由CSDN通过智能技术生成

std::async

string fetchData_fromDb0( string _info){
    this_thread::sleep_for( chrono::seconds( x);
    return "data from db0";
}

string fetchData_fromDb1( string _info){
    this_thread::sleep_for( chrono::seconds( x));
    return "data from db1";
}

假如我们的主线程是: string db0 = fetchData_fromDb0( ""); string db1 = fetchData_fromDb1( "");
这自然会消耗: x + x 的时间


我们可以并行的运行这2个函数;
(一种方式是: 我们手动的 创建一个线程) (一种方式是: 使用async函数)

我们这里先介绍下: (async方式)

future< string> db0_future = async( launch::async, fetchData_fromDb0, "info0");
' 他会自动创建一个线程(比如称为0), 此时, fetch0函数就开始执行了 '

future< string> db1_future = async( launch::async, fetchData_fromDb1, "info1");
' 他会自动创建一个线程(比如称为1), 此时, fetch1函数就开始执行了 '

' 此时的状态是:  子线程0 和 1 和 主线程, 3个 在并行运行cpu '

string db0_data = db0_future.get(); 
' 一旦调用get(), 主线程会卡死, 直到子线程0结束;  即此时状态是: 主线程会卡住, 即子线程0和1, 共2个 在并行运行cpu '

' 此时, 子线程0结束了, 即主线程 和 子线程1, 共2个 在并行运行cpu '

string db1_data = db1_future.get();
' 此时, 主线程再次卡死, 直到 子线程1结束;  即此时, 只有子线程1, 1个 在运行cpu '

' 此时, 只有主线程运行; ' 

cout << db0_data << ", "<< db1_data; 

此时, 我们发现, 原先是需要(x + x)的时间, 现在是: (x)的时间 可以通过, clock() 测试下

std::launch

从上面的使用中, 我们发现: future< string> db0_future = async( launch::async, fetchData_fromDb0, "info0")

解释下: future< 函数func的返回值> fut = async( launch::async/deferred, func, 函数func的参数)


第一参数是: std::launch 的枚举

  • async: 表示, 执行async这个函数时, 就立刻 开始异步(创建新线程)的 执行func函数
  • deferred: 执行async函数时, 不会立刻执行func函数;
    直到你执行fut.get()时, 会以非异步(不会创建新线程, 而是在主线程)执行func函数; (他还是单线程模式!!!)
    future< string> fut = async( launch::deferred, addressof( func));
    aaa;
    string ret = fut.get();
    
    ' 以上写法, 完全等价于: '
    
    aaa;
    string ret = func();
    
    这个deferred, 看似很没有呀… 一会讲他.

future

对于一个空future对象: future< ..> fut;, 他的valid()是false!!!
就意味着, 你调用: fut.get() 或 wait_for(), 都会抛出异常!!!

一般, future 我们不会自己创建他, 都是系统创建的, 然后我们copy对象
future< int> fut = xx.get_future(); xx是系统函数

future.wait_for()

需求: 一边执行func函数, 一边 每隔1s 提示用户(正在执行中…); 等执行完函数, 等待用户录入信息

可以知道, 这肯定是(多线程环境); 即为: async( launch::async)方式; 而不是launch::deferred 因为他是单线程;


future 顾名思义, 即未来;
他有几个作用:

  • 子线程执行完后, 会将返回值 存储到 future.get()里面
  • future里有一个future_status的枚举, 他表示: 当前子线程的状态
    • future_status::ready: 子线程已经执行结束, 返回值在future.get()里;

      关于fut.get(), 多讲一点:

      • (如果1, 子线程已经结束, 则立刻将子线程的返回值 通过get()返回)
      • (否则2, 子线程正在执行, 则将 调用者线程(即, fut所处在的线程) 给卡死, 直到 子线程执行完成;
        这是很当然的, 因为, 你调用get(), 就是要获取 (子线程)的返回值, 而他现在正在执行,
        当然需要将调用者线程给卡死, 等待他的结果出来
    • future_status::timeout: 子线程还正在执行

    • future_status::deferred: 这个只针对launch::deferred的async调用; 对于launch::async方式, 不会有这个状态;

future_status st = future.wait_for( 1s);

  • 当前线程(这行代码所在线程), 卡死1s后, 将子线程的状态 给返回

看到wait_for, 就应该知道, 肯定有一个wait_until;
这两个函数, 不仅在这里, 但很多标准库里都有; 而且, 这两个函数, 是 完全一样的!!! 两者可以完全等效的替换

在任何场景下, wait_for( x) == wait_unitl( current_time + x)


future< string> fut = async( launch::async, addressof( func)); ' func就开始 在(新子线程)里, 执行开始了 '

chrono::milliseconds span( 1000);
while( true){
   future_status st = fut.wait_for( span);
   if( st == future_status::ready){
       cout << "函数func执行结束" << endl;
       cout << "返回值为: " << fut.get() << endl;
       break;
   }
   else if( st == future_status::timeout){
       cout << "函数func, 正在执行中..." << endl;
   }
   else if( st == future_status::deferred){ ' 这里, 因为是launch::async方式, 不会出现这种情况 '
       cout << "函数func, 尚未开始执行" << endl;
   } 
}

以上代码的现象是:

  • func 和 主线程, 并行执行cpu
  • 每隔1s, 主线程 询问下 子线程的状态; (如果1, 正在执行, 则输出...) (如果2, 执行结束, 则break) 不会有第3种情况;

valid

future_status wait_for( ..){
	if( false == this->valid()){
		throw future_error();
	}
	...	
}

对于一个空future对象: future< ..> fut;, 他的valid()是false!!!
就意味着, 你调用: fut.get() 或 wait_for(), 都会抛出异常!!!

一般, future 我们不会自己创建他, 都是系统创建的, 然后我们copy对象
future< int> fut = xx.get_future(); xx是系统函数

future.valid, future.get(), future.wait()

对于launch::async方式, 只要没有调用过fut.get(), 则fut.valid() == true, 表示, 该future是有效的
一旦调用过 fut.get(), 则, fut.valid() == false


future.get() 只能调用1次!!! 不能调用多次!!! 否则, 会抛出 future_error异常

T future::get(){
	if( this->valid() == false){
		throw future_error();
	}
	this->wait();
	return ret_value;
}

future::wati()的作用是: (只要子线程没有结束, 则让调用者线程, 一直卡死)

内部成员变量: 共享状态/数据

从上面的: future< string> fut = async( launch::async, addressof( func));, 可以知道几个信息:

  • async函数里, 有一个future对象(比如命名为sub_fut) 他以返回值的方式, 传递给了 我们当前主线程的fut对象;
  • 在当前主线程 和 新子线程, 两者并行运行的期间, fut里的状态 (子线程正在执行 或 执行结束), 会动态更新
    而, 我们的 (当前主线程) 就没有修改过这个fut对象, 这说明, future里, 肯定有 (指针/引用)方式 来指向 (子线程)里的 状态;
State * ptr_state;   ' 指向, 子线程里的 状态 '
Result & ref_res;    ' 指向, 子线程里的 返回值 '

这个两个变量, 都是: (不同线程间, 使用 同一个内存) 的情况;

promise

我们从上面的future 知道, 我们不会自己创建一个future对象; 都是直接: auto fut = xx.get_future()
即, future都是 系统给我们维护的;

那么, 哪些类/函数里, 会自动给我们创建future呢?


promise类, 他里面就有个future对象;

promise< T> pro; 此时, pro.get_future() 他的状态是:

  • valid 有效的(我们知道, 我们自己创建的future都是invalid的, 只有系统里的future 才会是valid)`
  • timeout 表示: future里的数据(get()), 还没有设置

promise"承诺", 承诺 在(某个时间) 会调用set_value 或 set_value_at_thread_exit
一旦set_value后, future的状态 就更新为: ready, 表示: future里有数据了

获取线程返回值

我们之前介绍了, 通过async函数 来获取 , 子线程的返回值;

再介绍一种:

void func( promise< string> & pro){
	...
	pro.set_value( "thread return date");
	' 此时, pro.get_future()的状态会变为{从timeout 变成 ready}; 这个string返回值, 也会放到future().get()里 '
	' 一般会使用: pro.set_value_at_thread_exit( ..); 因为, 上面的set_value() 回立刻改变 pro.get_future() '
	' 即, 外界立刻就得到了返回值; 因为一般概念下, 得到函数的返回值, 就意味着: 这个函数已经执行结束 '
	' 而在这里, 如果使用set_value, 外界得到{返回值}, 会误以为: 这个函数(即子线程) 已经执行完毕; '
	' 此时不然!!! set_value的下面, 还有代码, 此时函数并未结束 子线程也并未结束; '
	' 所以, 使用set_value_at_thread_exit更好!! 等这个函数执行完毕后, 才会更新future里的内容 '
}

int main(){
    promise< string> pro;
    thread th( addressof( func), ref( pro));
    future< string> fut = pro.get_future();

    while( fut.wait_for( chrono::seconds( 1)) != future_status::ready){ ' 等于: timeout (表示: future等待赋值) '
        cout << "thread is working..." << endl;
    }
    
    cout << "main-thread receive the sub-thread return data: " << fut.get() << endl;
    
    th.join(); ' 只要是一个线程, 就要写: 要么join, 要么detach '
}

set_value, set_value_at_thread_exit

set_value函数, 只能调用1次!!!

  • 因为, 初始时 promise里的future对象, 他的status状态是: timeout (表示: future里的数据, 还没有设置值)
  • 而当你set_value后, future对象 有值了, 他的status状态是: ready (表示: future里有数据了)
  • future, 只有1个返回值, 只能调用1次set_value

get_future

get_future()函数, 只能调用1次!!!

  • 否则会抛出异常: Future already retrieved, 这个对象 只能索引/获取 1次,
    因为我们知道, future里 有很多共享数据 (是跨线程的共享) 比如: 状态, 返回值
    而且, future的 赋值/拷贝 都是delete掉了的; 只有(移动) 赋值/拷贝, 是可以的;
    因为有(跨线程的)共享数据, 只允许有1个 future对象, 这样设计很安全
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值