@@ -14926,7 +14926,7 @@ This section looks at passing messages so that a programmer doesn't have to do e
14926
14926
Message passing rules summary:
14927
14927
14928
14928
* [CP.60: Use a `future` to return a value from a concurrent task](#Rconc-future)
14929
- * [CP.61: Use an `async()` to spawn a concurrent task ](#Rconc-async)
14929
+ * [CP.61: Use `async()` to spawn concurrent tasks ](#Rconc-async)
14930
14930
* message queues
14931
14931
* messaging libraries
14932
14932
@@ -14954,12 +14954,13 @@ There is no explicit locking and both correct (value) return and error (exceptio
14954
14954
14955
14955
???
14956
14956
14957
- ### <a name="Rconc-async"></a>CP.61: Use an `async()` to spawn a concurrent task
14957
+ ### <a name="Rconc-async"></a>CP.61: Use `async()` to spawn concurrent tasks
14958
14958
14959
14959
##### Reason
14960
14960
14961
- A `future` preserves the usual function call return semantics for asynchronous tasks.
14962
- There is no explicit locking and both correct (value) return and error (exception) return are handled simply.
14961
+ Similar to [R.12](#Rr-immediate-alloc), which tells you to avoid raw owning pointers, you should
14962
+ also avoid raw threads and raw promises where possible. Use a factory function such as `std::async`,
14963
+ which handles spawning or reusing a thread without exposing raw threads to your own code.
14963
14964
14964
14965
##### Example
14965
14966
@@ -14974,25 +14975,63 @@ There is no explicit locking and both correct (value) return and error (exceptio
14974
14975
14975
14976
void async_example()
14976
14977
{
14977
- try
14978
- {
14979
- auto v1 = std::async(std::launch::async, read_value, "v1.txt");
14980
- auto v2 = std::async(std::launch::async, read_value, "v2.txt");
14981
- std::cout << v1.get() + v2.get() << '\n';
14982
- }
14983
- catch (std::ios_base::failure & fail)
14984
- {
14978
+ try {
14979
+ std::future<int> f1 = std::async(read_value, "v1.txt");
14980
+ std::future<int> f2 = std::async(read_value, "v2.txt");
14981
+ std::cout << f1.get() + f2.get() << '\n';
14982
+ } catch (const std::ios_base::failure& fail) {
14985
14983
// handle exception here
14986
14984
}
14987
14985
}
14988
14986
14989
14987
##### Note
14990
14988
14991
- Unfortunately, `async()` is not perfect.
14992
- For example, there is no guarantee that a thread pool is used to minimize thread construction.
14993
- In fact, most current `async()` implementations don't.
14994
- However, `async()` is simple and logically correct so until something better comes along
14995
- and unless you really need to optimize for many asynchronous tasks, stick with `async()`.
14989
+ Unfortunately, `std::async` is not perfect. For example, it doesn't use a thread pool,
14990
+ which means that it may fail due to resource exhaustion, rather than queueing up your tasks
14991
+ to be executed later. However, even if you cannot use `std::async`, you should prefer to
14992
+ write your own `future`-returning factory function, rather than using raw promises.
14993
+
14994
+ ##### Example (bad)
14995
+
14996
+ This example shows two different ways to succeed at using `std::future`, but to fail
14997
+ at avoiding raw `std::thread` management.
14998
+
14999
+ void async_example()
15000
+ {
15001
+ std::promise<int> p1;
15002
+ std::future<int> f1 = p1.get_future();
15003
+ std::thread t1([p1 = std::move(p1)]() mutable {
15004
+ p1.set_value(read_value("v1.txt"));
15005
+ });
15006
+ t1.detach();
15007
+
15008
+ std::packaged_task<int()> pt2(read_value, "v2.txt");
15009
+ std::future<int> f2 = pt2.get_future();
15010
+ std::thread(std::move(pt2)).detach();
15011
+
15012
+ std::cout << f1.get() + f2.get() << '\n';
15013
+ }
15014
+
15015
+ ##### Example (good)
15016
+
15017
+ This example shows one way you could follow the general pattern set by
15018
+ `std::async`, in a context where `std::async` itself was unacceptable for
15019
+ use in production.
15020
+
15021
+ void async_example(WorkQueue *wq)
15022
+ {
15023
+ std::future<int> f1 = wq->enqueue([]() {
15024
+ return read_value("v1.txt");
15025
+ });
15026
+ std::future<int> f2 = wq->enqueue([]() {
15027
+ return read_value("v2.txt");
15028
+ });
15029
+ std::cout << f1.get() + f2.get() << '\n';
15030
+ }
15031
+
15032
+ Any threads spawned to execute the code of `read_value` are hidden behind
15033
+ the call to `WorkQueue::enqueue`. The user code deals only with `future`
15034
+ objects, never with raw `thread`, `promise`, or `packaged_task` objects.
14996
15035
14997
15036
##### Enforcement
14998
15037
0 commit comments