C++——并发之std::async使用注意的坑,以及promise,future

promise背景1
promise背景2
async

1. async 与promise,future

1.1 async以及获取时间

async函数如下所示

  template<typename _Fn, typename... _Args>
    future<__async_result_of<_Fn, _Args...>>
    async(launch __policy, _Fn&& __fn, _Args&&... __args)
    {
      std::shared_ptr<__future_base::_State_base> __state;
      if ((__policy & launch::async) == launch::async)
	{
	  __try
	    {
	      __state = __future_base::_S_make_async_state(
		  std::thread::__make_invoker(std::forward<_Fn>(__fn),
					      std::forward<_Args>(__args)...)
		  );
	    }
#if __cpp_exceptions
	  catch(const system_error& __e)
	    {
	      if (__e.code() != errc::resource_unavailable_try_again
		  || (__policy & launch::deferred) != launch::deferred)
		throw;
	    }
#endif
	}
      if (!__state)
	{
	  __state = __future_base::_S_make_deferred_state(
	      std::thread::__make_invoker(std::forward<_Fn>(__fn),
					  std::forward<_Args>(__args)...));
	}
      return future<__async_result_of<_Fn, _Args...>>(__state);
    }

std::async的操作,其实相当于封装了std::promise、std::packaged_task加上std::thread。

返回值为future对象,第一个参数为policy,第二个为新起线程执行的函数,第三个参数为函数参数,其作用为另起一个线程,执行函数,并且返回future,实现和主线程的同步。主线程中执行future::get,完成线程同步。

launch有如下三种情况:

  1. std::launch::async 传递的可调用对象异步执行;
  2. std::launch::deferred 传递的可调用对象同步执行;当调用get时,async才执行
  3. 混合情况

返回类型:
future_status有三种状态:

  1. deferred:异步操作还没开始
  2. ready:异步操作已经完成
  3. timeout:异步操作超时

获取future结果有三种方式:get、wait、wait_for,其中get等待异步操作结束并返回结果,wait只是等待异步操作完成,没有返回值,wait_for是超时等待返回结果。

注意:future::wait_for时间到后,返回的是timeout状态,当async里is_prime函数执行结束,为ready状态
在这里插入图片描述

#include <future>
#include <iostream>
#include <time.h>
std::thread::id fun(int i)
{
    std::cout<<"go!"<<std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout<<"hello"<<i<<std::endl;
    std::thread::id tid =  std::this_thread::get_id();
    std::cout<<tid<<std::endl;
    return tid;
}
class A
{
    public:
    private:
        int b=2;
};
int main()
{
    A a;
    int* b = reinterpret_cast<int*>(&a);
    std::cout<<*b<<std::endl;
     time_t time1 = time(NULL);
    auto now1 =  std::chrono::system_clock::now();
    std::future<std::thread::id> fu = std::async(std::launch::async,fun,2018); //注意这里,std::launch::deferred为get时刻开始运行
    std::cout<<"hello"<<std::endl;
    std::chrono::milliseconds spans(5000);
    std::this_thread::sleep_for(spans);
    // while(fu.wait_for(spans)!=std::future_status::ready)
    //     std::cout<<" . ";
    std::cout << std::endl;
    std::thread::id i = fu.get();
    pthread_t*  tid = reinterpret_cast<pthread_t*>(&i);
    std::cout<<*tid<<std::endl;
    time_t time2 = time(NULL);
    auto now2 =  std::chrono::system_clock::now();
    auto du = std::chrono::duration_cast<std::chrono::milliseconds>(now2-now1);
    std::cout<<static_cast<float>(du.count())/static_cast<float>(1000)<<std::endl;

     std::cout<<time2-time1<<std::endl;
    return 0;
}

1.2 packaged_task

观察packaged_task源码,其与future一致,均不能拷贝

  template<typename _Res, typename... _ArgTypes>
    class packaged_task<_Res(_ArgTypes...)>
    {
      typedef __future_base::_Task_state_base<_Res(_ArgTypes...)> _State_type;
      shared_ptr<_State_type>                   _M_state;

    public:
      // Construction and destruction
      packaged_task() noexcept { }

      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 2095.  missing constructors needed for uses-allocator construction
      template<typename _Allocator>
	packaged_task(allocator_arg_t, const _Allocator& __a) noexcept
	{ }

      template<typename _Fn, typename = typename
	       __constrain_pkgdtask<packaged_task, _Fn>::__type>
	explicit
	packaged_task(_Fn&& __fn)
	: packaged_task(allocator_arg, std::allocator<int>(),
			std::forward<_Fn>(__fn))
	{ }
      // No copy
      packaged_task(const packaged_task&) = delete;
      packaged_task& operator=(const packaged_task&) = delete;
      // Result retrieval
      future<_Res>
      get_future()
      { return future<_Res>(_M_state); }

packaged_task传入函数指针等,然后get_future,再通过ref传入thread。在主线程中调用get

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

int fun(int i)
{
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout<<"hello"<<i<<std::endl;
    return i;
}
int main()
{
    std::packaged_task<int(int)> pt(fun);
    std::future<int> fu = pt.get_future();
    std::thread th(std::move(pt),22);
    std::cout<<"sha???"<<std::endl;
    int i = fu.get();
    std::cout<<"world"<<i<<std::endl;
    th.join();
    return 0;
}

1.3 promise与future

1.3.1 future源码

future函数源码如下:

  /// Primary template for future.
  template<typename _Res>
    class future : public __basic_future<_Res>
    {
      friend class promise<_Res>;
      template<typename> friend class packaged_task;
      template<typename _Fn, typename... _Args>
        friend future<__async_result_of<_Fn, _Args...>>
        async(launch, _Fn&&, _Args&&...);

      typedef __basic_future<_Res> _Base_type;
      typedef typename _Base_type::__state_type __state_type;

      explicit
      future(const __state_type& __state) : _Base_type(__state) { }

    public:
      constexpr future() noexcept : _Base_type() { }

      /// Move constructor
      future(future&& __uf) noexcept : _Base_type(std::move(__uf)) { }

      // Disable copying
      future(const future&) = delete;
      future& operator=(const future&) = delete;

      future& operator=(future&& __fut) noexcept
      {
        future(std::move(__fut))._M_swap(*this);
        return *this;
      }

      /// Retrieving the value
      _Res
      get()
      {
        typename _Base_type::_Reset __reset(*this);
        return std::move(this->_M_get_result()._M_value());
      }

      shared_future<_Res> share() noexcept;
    };

注意future是不能拷贝的,所以要move或者ref
在这里插入图片描述

1.3.2 promise

std::promise的作用就是提供一个不同线程之间的数据同步机制,它可以存储一个某种类型的值,并将其传递给对应的future, 即使这个future不在同一个线程中也可以安全的访问到这个值。一个promise只能被获取到一次。

  1. std::promise::get_future:返回一个与promise共享状态相关联的future对象
  2. std::promise::set_value:设置共享状态的值,此后promise共享状态标识变为ready
  3. std::promise::set_exception:为promise设置异常,此后promise的共享状态标识变为ready
  4. std::promise::set_value_at_thread_exit:设置共享状态的值,但是不将共享状态的标志设置为 ready,当线程退出时该 promise 对象会自动设置为 ready(注意:该线程已设置promise的值,如果在线程结束之后有其他修改共享状态值的操作,会抛出future_error(promise_already_satisfied)异常)
  5. std::promise::swap:交换 promise 的共享状态
    在这里插入图片描述
#include <future>
#include <iostream>
#include <thread>
#include <chrono>
void fun(std::future<int> &f)
{
    int x = f.get();
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout<<"hello"<<x<<std::endl;
}

int main()
{
    std::promise<int> pro;
    std::future<int> fu = pro.get_future();
    std::thread th1(fun,std::ref(fu));
    pro.set_value(5);
    th1.join();


    return 0;
}

2. async的阻塞情况分析

之前项目中使用了async,但是遇到了坑,async发生了阻塞,后来改用thread解决了。查询之后发现:
在这里插入图片描述
future对象析构的时候,可能发生阻塞:

  1. 共享状态是通过std::async创建
  2. 共享状态还不是ready状态
  3. 被析构的future对象是共享状态的最后一个引用

如果直接使用std::async则,很有可能发生阻塞,因为其产生了临时变量future,析构时发现非ready,则产生阻塞
当共享状态是ready时候,解除阻塞,完成析构

为了避免上述问题,可以延长async的返回值的生命周期,比如:将future类型对象变为返回值等
future< int> fun()
{
auto fut1 = std::async…;
return fun1;
}

2.1 例子分析1

在这里插入图片描述
分析上面代码,按照设计目的,This shold show before work done 2!?应该是在work done 2!之前执行,但是这里async发生了阻塞,等sleep_for完成后,future_status状态变成ready,后future析构完成,才能执行输出

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
std::future<int> funasync()
{
    auto fut1 = std::async(std::launch::async, [] { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "work done 1!\n"; 
    return 1;});
    std::cout << "Work done - implicit join on fut1 associated thread just ended\n\n";
    std::cout << "Test 2 start" << std::endl;
    std::async(std::launch::async, [] { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "work done 2!" << std::endl; });
    std::cout << "This shold show before work done 2!?" << std::endl;
    return fut1;
}

int funpack()
{
    std::thread th([](){
        std::this_thread::sleep_for(std::chrono::milliseconds(3000));
        std::cout << "funpack start" << std::endl;
        return 1;
    });
    th.detach();
}

int main() {
    using namespace std::literals;
    funasync();
    std::packaged_task<int(void)> pt(funpack);
    std::future<int> fu = pt.get_future();
    //std::thread ts(std::move(pt));
    pt();
    std::future_status st = fu.wait_for(std::chrono::seconds(6));//wait_for可设置超时时间,如果在超时时间之内任务完成
                                                        //,则返回std::future_status::ready状态;如果在超时时间之内任务尚未完成,则返回std::future_status::timeout状态

	if (st == std::future_status::ready)
	{
		std::cout << "~~~~" << std::endl;
	}	 
	else if (st == std::future_status::timeout)
	{
		std::cout << "·····" << std::endl;
	}
	else if (st == std::future_status::deferred)
	{
		std::cout << "11111" << std::endl;
    }
    int a = fu.get();
    std::cout << a << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    std::cout << "end main" << std::endl;
    return 0;
}

结果如下所示:

xili271948@er04180p:/data/lxz/tcp$ ./stdasync
Work done - implicit join on fut1 associated thread just ended

Test 2 start
work done 1!
work done 2!
This shold show before work done 2!?
~~~~
0
funpack start
end main
xili271948@er04180p:/data/lxz/tcp$

2.2 例子分析2

在这里插入图片描述

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
std::future<int> funasync()
{
    auto fut1 = std::async(std::launch::async, [] { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "work done 1!\n"; 
    return 1;});
    std::cout << "Work done - implicit join on fut1 associated thread just ended\n\n";
    std::cout << "Test 2 start" << std::endl;
   {auto fut2 = std::async(std::launch::async, [] { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "work done 2!" << std::endl; });
    std::cout << "async2 " << std::endl;
    }
    std::cout << "This shold show before work done 2!?" << std::endl;
    return fut1;
}

int funpack()
{
    std::thread th([](){
        std::this_thread::sleep_for(std::chrono::milliseconds(3000));
        std::cout << "funpack start" << std::endl;
        return 1;
    });
    th.detach();
}

class Date
{
    private:
    int year_, month_, date_;
    public:
    Date(int year,int month,int date):date_(date),month_(month),year_(year){}
    friend void operator<<(std::ostream&,Date&);
};

void operator<<(std::ostream& out,Date& data)
{
    out<<data.date_<<data.month_<<data.year_<<std::endl;
}
int main() {
    Date mydata(2020,12,21);
    std::ostream& out = std::cout;
    out<<mydata;

    out<<"out"<<std::endl;
    // using namespace std::literals;
    funasync();
    std::packaged_task<int(void)> pt(funpack);
    std::future<int> fu = pt.get_future();
    //std::thread ts(std::move(pt));
    pt();
    std::future_status st = fu.wait_for(std::chrono::seconds(1));//wait_for可设置超时时间,如果在超时时间之内任务完成
                                                        //,则返回std::future_status::ready状态;如果在超时时间之内任务尚未完成,则返回std::future_status::timeout状态

	if (st == std::future_status::ready)
	{
		std::cout << "~~~~" << std::endl;
	}	 
	else if (st == std::future_status::timeout)
	{
		std::cout << "·····" << std::endl;
	}
	else if (st == std::future_status::deferred)
	{
		std::cout << "11111" << std::endl;
    }
    int a = fu.get();
    std::cout << a << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    std::cout << "end main" << std::endl;
    return 0;
}

结果如下所示:

xili271948@er04180p:/data/lxz/tcp$ ./stdasync
21122020
out
Work done - implicit join on fut1 associated thread just ended

Test 2 start
async2
work done 1!
work done 2!
This shold show before work done 2!?
late
~~~~
0
funpack start
end main

  • 11
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
是的,C++ 11 引入了 std::async 函数,可以用它来创建异步任务。std::async 函数的原型如下所示: ```c++ template <class Function, class... Args> std::future<typename std::result_of<Function(Args...)>::type> async(Function&& f, Args&&... args); ``` std::async 函数的作用是创建一个异步任务,并返回一个 std::future 对象,用于获取异步任务的返回值。std::async 函数的第一个参数是要执行的函数,后面的参数是该函数的参数。该函数会在一个新线程中执行,并在执行完成后返回结果。如果该函数抛出异常,则 std::future 对象中会保存该异常信息。 std::async 函数还可以指定执行策略,例如 std::launch::async 表示在新线程中执行异步任务,std::launch::deferred 表示在调用 std::future::get() 函数时执行异步任务。如果不指定执行策略,则由编译器自行决定。 以下是一个使用 std::async 函数创建异步任务的示例: ```c++ #include <iostream> #include <future> int foo(int x) { std::cout << "foo is running in thread " << std::this_thread::get_id() << std::endl; return x + 1; } int main() { std::future<int> result = std::async(std::launch::async, foo, 1); std::cout << "main is running in thread " << std::this_thread::get_id() << std::endl; std::cout << "result is " << result.get() << std::endl; return 0; } ``` 在上面的示例中,使用 std::async 函数创建了一个异步任务 foo,并将参数 1 传递给该函数。执行结果会保存在 std::future 对象中,可以使用 std::future::get() 函数获取异步任务的返回值。在主线程中也输出了一些信息,用于对比异步任务和主线程中的执行情况。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值