C++11多线程之promise

📅 2026/7/16 1:11:11
C++11多线程之promise
原文http://en.cppreference.com/w/cpp/thread/promisehttp://en.cppreference.com/w/cpp/thread/promise/promisehttp://en.cppreference.com/w/cpp/thread/promise/~promisehttp://en.cppreference.com/w/cpp/thread/promise/operator%3Dhttp://en.cppreference.com/w/cpp/thread/promise/swaphttp://en.cppreference.com/w/cpp/thread/promise/get_futurehttp://en.cppreference.com/w/cpp/thread/promise/set_valuehttp://en.cppreference.com/w/cpp/thread/promise/set_value_at_thread_exithttp://en.cppreference.com/w/cpp/thread/promise/set_exceptionhttp://en.cppreference.com/w/cpp/thread/promise/set_exception_at_thread_exithttp://en.cppreference.com/w/cpp/thread/promise/swap2promise定义在头文件 future 中。(1) 基本模板template class promise;(2) 非void特化用于不同线程间的对象的通信template class promiseR(3) void特化用于无状态时线程间的通信template class promise;类模板 std::promise 提供了一种能力存储一个值或异常而该值或异常将来是被异步地获得而获得的方法是通过 std::promise 对象创建 std::future 对象再由future对象异步地提供该值或类型。每一个 promise 都是关联一个共享状态shared state的该共享状态包含了一些状态信息和一个也许还没有被evaluate的结果会被evaluate成一个值或者异常。一个promise可能会对共享状态做3件事情使准备好make ready promise将值或者异常存储在共享状态中。将状态标记为ready并且将所有等待在和该共享状态关联的future上的线程解除阻塞。释放release promise放弃对共享状态的引用reference。如果这是最后一个这样的引用那么这个共享状态将被销毁。除非这个共享状态是被一个尚未ready的std::async所创建的那么这个操作将不会阻塞。放弃abandon promise存储错误码为std::future_errc::broken_promise的std::future_error类型的异常并使得共享状态ready然后释放它。promise是promise-future通信渠道的“推动”端(“push” end) 这个操作存储一个值到共享状态。这个操作和一些成功的返回同步。这些“成功的返回”指的是从一些等待在共享状态上的函数比如std::future::get返回。对同一个共享状态的并发访问可能会冲突比如std::shared_future::get的多个调用者必须全部都是只读的或者提供额外的同步。1. 构造函数默认构造函数。使用一个空的共享状态来构造这个promise.promise();使用一个空的共享状态来构造这个promise. 这个共享状态是被alloc给分配的。Alloc必须满足Allocator的要求。templatepromise( std::allocator_arg_t, const Alloc alloc);Move构造函数。使用move语义用other的共享状态来构造promise. 构造之后other将没有共享状态。promise( promise other);promise不是copyable的。promise(const promise other) delete;2. 析构函数~promise();放弃共享状态。如果共享状态是ready的释放release它。如果共享状态是没有ready的存储一个异常std::future_error类型带错误码std::future_errc::broken_promise使共享状态为ready然后再release它。3. std::promise::operatorMove赋值操作符。首先放弃共享状态如同析构函数然后将other的共享状态赋值过来就如同执行了std::promise(std::move(other)).swap(*this).promise operator( promise other );promise不是拷贝可赋值的not copy-assignable。promise operator( const promise rhs ) delete;4. std::promise::swap交换2个promise对象的共享状态。void swap( promise other );5. std::promise::get_futurestd::future get_future();返回一个future对象该对象所关联的共享状态和*this所关联的共享状态是同一个。如果 *this 没有共享状态或者get_future已经被调用了就会抛出异常。为了得到promise-future通信渠道的多个“弹出”端pop end要使用 std::future::shared.异常std::future_error 在下列条件满足时被抛出*this 没有共享状态。错误分类被设置为 no_state.get_future() 已经被一个拥有相同共享状态的promise调用。错误分类被设置为 future_already_retrieved.6. std::promise::set_value(1) void set_value( const R value );(2) void set_value( R value );(3) void set_value( R value );(4) set_value();原子性地将值存储到共享状态并使得共享状态ready.该操作就如同set_value, set_exception, set_value_at_thread_exit, set_exception_at_thread_exit 在更新promise对象时获得了一个和该promise对象相关联的唯一mutex.如果没有共享状态或者共享状态已经存储了一个值或者异常就会有异常被抛出。异常std::future_error 在下列条件时会被抛出*this 没有共享状态。错误分类被设置为 no_state.共享状态已经存储了一个值或者异常。错误分类被设置为 promise_already_satisfied.另外(1)和(2): 任何被值的拷贝构造函数抛出的异常也会继续被抛出。(3): 任何被值的move构造函数抛出的异常也会继续被抛出。示例程序#includethread#includefuture#includecctype#includevector#includealgorithm#includeiterator#includeiostream#includesstreamintmain(){std::istringstream iss_numbers{3 4 1 42 23 -23 93 2 -289 93};std::istringstream iss_letters{ a 23 b,e a2 k k?a;si,ksa c};std::vectorintnumbers;std::vectorcharletters;std::promisevoidnumbers_promise,letters_promise;autonumbers_readynumbers_promise.get_future();autoletter_readyletters_promise.get_future();std::threadvalue_reader([]{// I/O operations.std::copy(std::istream_iteratorint{iss_numbers},std::istream_iteratorint{},std::back_inserter(numbers));//Notify for numbers.numbers_promise.set_value();std::copy_if(std::istreambuf_iteratorchar{iss_letters},std::istreambuf_iteratorchar{},std::back_inserter(letters),::isalpha);//Notify for letters.letters_promise.set_value();});numbers_ready.wait();std::sort(numbers.begin(),numbers.end());if(letter_ready.wait_for(std::chrono::seconds(1))std::future_status::timeout){//output the numbers while letters are being obtained.for(intnum:numbers)std::coutnum ;numbers.clear();//Numbers were already printed.}letter_ready.wait();std::sort(letters.begin(),letters.end());//If numbers were already printed, it does nothing.for(intnum:numbers)std::coutnum ;std::cout\n;for(charlet:letters)std::coutlet ;std::cout\n;value_reader.join();}/** Output: -289 -23 1 2 3 4 23 42 93 93 a a a a b c e i k k k s s **/7. std::promise::set_value_at_thread_exit(1) void set_value_at_thread_exit(const R value);(2) void set_value_at_thread_exit(R value);(3) void set_value_at_thread_exit(R value);(4) void set_value_at_thread_exit();把值写入共享状态但并不立即使得该共享状态ready. 当该线程退出的时候在所有thread-lock的变量都被销毁的时候该共享状态才变为ready.本操作就如同当更新一个promise对象的时候set_value, set_exception, set_value_at_thread_exit和set_exception_at_thread_exit获得了一个和该promise对象相关的mutex. (The operation behaves as though set_value, set_exception, set_value_at_thread_exit, and set_exception_at_thread_exit acquire a single mutex associated with the promise object while updating the promise object.)如果没有共享状态或者共享状态已经存储了一个值或者异常就会有异常抛出。异常std::future_error 会在下列条件下抛出*this 没有共享状态。错误分类将被设为no_state.共享状态已经存储了一个值或者异常。错误分类被设置为 promise_already_satisfied.另外1-2) 值的拷贝构造函数所抛出的异常也会被抛出。值的move构造函数所抛出的异常也会被抛出。示例代码#includeiostream#includefuture#includethreadintmain(){// using namespace std::chrono_literals;std::promiseintp;std::futureintfp.get_future();std::thread([p]{// std::this_thread::sleep_for(1s);std::this_thread::sleep_for(std::chrono::seconds(1));p.set_value_at_thread_exit(9);}).detach();std::coutWaiting...std::flush;f.wait();std::coutDone!\nResult is: f.get()\n;}/** Output Waiting...Done! Result is: 9 **/8. std::promise::set_exceptionvoid set_exception(std::exception_ptr p);原子性地将异常指针p写入共享状态并使得共享状态成为ready状态。本操作就如同当更新一个promise对象的时候set_value, set_exception, set_value_at_thread_exit和set_exception_at_thread_exit获得了一个和该promise对象相关的mutex. (The operation behaves as though set_value, set_exception, set_value_at_thread_exit, and set_exception_at_thread_exit acquire a single mutex associated with the promise object while updating the promise object.)如果没有共享状态或者共享状态已经存储了一个值或者异常就会有异常抛出。异常std::future_error 发生在以下条件时*this 没有共享状态。错误分类将被设为no_state.共享状态已经存储了一个值或者异常。错误分类被设置为 promise_already_satisfied.示例程序#includethread#includeiostream#includefutureintmain(){std::promiseintresult;std::threadt([]{try{// code that may throwthrowstd::runtime_error(Example);}catch(...){try{// store anything thrown in the promiseresult.set_exception(std::current_exception());}catch(...){}// set_exception() may throw too}});try{std::coutresult.get_future().get();}catch(conststd::exceptione){std::coutException from the thread: e.what()\n;}t.join();}/** Output: Exception from the thread: Example **/9. std::promise::set_exception_at_thread_exitvoid set_exception_at_thread_exit(std::exception_ptr p);将异常指针p写入共享状态但并不使得共享状态立即变为ready. 当前线程退出的时候共享状态才变为ready,也就是在所有thread-local生命周期的变量都被销毁之后。本操作就如同当更新一个promise对象的时候set_value, set_exception, set_value_at_thread_exit和set_exception_at_thread_exit获得了一个和该promise对象相关的mutex. (The operation behaves as though set_value, set_exception, set_value_at_thread_exit, and set_exception_at_thread_exit acquire a single mutex associated with the promise object while updating the promise object.)如果没有共享状态或者共享状态已经存储了一个值或者异常就会有异常抛出。异常std::future_error 发生在以下条件时*this 没有共享状态。错误分类将被设为no_state.共享状态已经存储了一个值或者异常。错误分类被设置为 promise_already_satisfied.10. std::swap(std::promise)定义在头文件 中。templatevoid swap( promise lhs, promise rhs);为std::promise而特化std::swap. 交换lhs和rhs的共享状态。高效地调用 lhs.swap(rhs).11. 总示例程序#includevector#includethread#includefuture#includenumeric#includeiostreamvoidaccumulate(std::vectorint::iterator first,std::vectorint::iterator last,std::promiseintaccumulate_promise){intsumstd::accumulate(first,last,0);accumulate_promise.set_value(sum);// Notify future}intmain(){std::vectorintnumbers{1,2,3,4,5,6};std::promiseintaccumulate_promise;std::futureintaccumulate_futureaccumulate_promise.get_future();std::threadwork_thread(accumulate,numbers.begin(),numbers.end(),std::move(accumulate_promise));accumulate_future.wait();// wait for resultstd::coutresultaccumulate_future.get()\n;work_thread.join();// wait for thread completion}/** Output: result21 **/