1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
In addition to tt(std::packaged_task) and tt(std::async) the class template
hi(promise)tt(std::promise) can be used to obtain the results from a
separate thread.
Before using the class template tt(promise) the tthi(future) header file
must be included.
A tt(promise) is used to obtain the results from another thread without
further synchronization requirements. Consider the following program:
verbinclude(-s4 //code examples/promise0.cc)
Chances are that this program shows the value 0: the tt(cout) statement has
already been executed before the detached thread has had a chance to complete
its work. In this example that problem can easily be solved by using a
non-detached thread, and using the thread's tt(join) member, but when multiple
threads are used that requires named threads and as many tt(join)
calls. Instead, using a tt(promise) might be preferred:
verbinclude(-ns4 //code examples/promise1.cc)
This example also uses a detached thread, but its results are kept for
future reference in a tt(promise) object, instead of directly being assigned
to a final destination variable. The tt(promise) object contains a tt(future)
object holding the computed value. The tt(future's get) member blocks until
the future has been made ready, at which point the result becomes
available. By then the detached thread may or may not yet have been
completed. If it already completed its work then tt(get) immediately returns,
otherwise there will be a slight delay.
Promises are useful when implementing a multi threaded version of some
algorithm without having to use additional synchronization statements. As an
example consider matrix multiplications. Each element of the resulting
product matrix is computed as the inner product of two vectors: the inner
product of a row of the left-hand matrix operand and a column of the
right-hand matrix operand becomes element tt([row][column]) of the resulting
matrix. Since each element of the resulting matrix can independently be
computed from the other elements, a multi threaded implementation is well
possible. In the following example the function tt(innerProduct) (lines 4..11)
leaves its result in a tt(promise) object:
verbinclude(-ns4 //code examples/promise2.cc)
Each inner product is computed by a separate (anonymous and detached)
thread (lines 17..21), which starts as soon as the run-time system allows it
to start. By the time the threads have finished the resulting inner products
can be retrieved from the promises' futures. Since futures' tt(get) members
block until their results are actually available, the resulting matrix can
simply be displayed by calling those members in sequence (lines 23..28).
So, a tt(promise) allows us to use a thread to compute a value (or
exception, see below), which value may then be collected by another thread at
some future point in time. The promise remains available, and as a consequence
further synchronization of the threads and the program starting the threads is
not necessary. When the promise object contains an exception, rather than a
value, its future's tt(get) member rethrows the stored exception.
Here is the class tt(promise's) interface. Note that the class tt(promise) is
a class template: its template type parameter tt(ReturnType) specifies the
template type parameter of the tt(std::future) that can be retrieved from it.
Constructors and destructor:
itemization(
ittq(promise())
(The default constructor constructs a tt(promise) object containing a
shared state. The shared state may be returned by the member
tt(get_future) (see below), but that future has not yet been made
ready;)
ittq(promise(promise &&tmp) noexcept)
(The move constructor constructs a tt(promise) object, transferring
the ownership of tt(tmp's) shared state to the newly constructed
object. After the object has been constructed, tt(tmp) no longer
contains a shared state;)
ittq(~promise())
(The object's shared state (if any) is abandoned;)
)
Member functions:
itemization(
ittq(std::future<ReturnType> get_future())
(A tt(std::future) object sharing the current object's shared state is
returned. A tt(future_error) exception is thrown upon error,
containing
itemization(
itt(future_already_retrieved) if tt(get_future) was already called on a
tt(packaged_task) object containing the same shared state as the
current object;
itt(no_state) if the current object has no shared state.
)
Note: Any tt(futures) that share the object's shared state may
access the result returned by the object's task;)
ittq(promise &operator=(promise &&rhs) noexcept)
(The move assignment operator first releases the current object's
shared state (if available), after which the current object and
tt(tmp) are swapped;)
ittq(void promise<void>::set_value())
(See below, at the last tt(set_value) member's description;)
ittq(void set_value(ReturnType &&value))
(See below, at the last tt(set_value) member's description;)
ittq(void set_value(ReturnType const &value))
(See the next member function's description;)
ittq(void set_value(ReturnType &value))
(The argument (tt(value)) is atomically stored in the shared state,
which is then also made ready. A tt(future_error) exception is thrown
upon error, containing
itemization(
itt(promise_already_satisfied) if the shared state has already been
made ready;
itt(no_state) if the current object does not have any shared state.
)
Alternatively, any exception thrown by tt(value)'s move or copy
constructor may be thrown;)
COMMENT(
When multiple threads use tt(set_value) and tt(set_exception) then
calls to set_value and set_exception on a single tt(promise) object
are serialized, and they synchronize and serialize with other
functions through the referred shared state.
Not overly important? When multiple set_value calls occur then a
promise_already_satisfied is thrown.
END)
ittq(void set_exception(std::exception_ptr obj))
(tt(Exception_ptr obj) (cf. section ref(EXCPTR)) is atomically stored
in the shared state, making that state ready. A tt(future_error)
exception is thrown upon error, containing
itemization(
itt(promise_already_satisfied) if the shared state has already been
made ready;
itt(no_state) if the current object does not have any shared state;
))
ittq(void set_exception_at_thread_exit(exception_ptr ptr))
(The exception pointer tt(ptr) is stored in the shared state without
immediately making that state ready. The state becomes ready when the
current thread exits, once all objects of thread storage duration
which are associated with the ending thread have been destroyed. A
tt(future_error) exception is thrown upon error, containing
itemization(
itt(promise_already_satisfied) if the shared state has already been
made ready;
itt(no_state) if the current object does not have any shared state;
))
ittq(void set_value_at_thread_exit())
(See below, at the last tt(set_value_at_thread_exit) member's
description;)
ittq(void set_value_at_thread_exit(ReturnType &&value))
(See below, at the last tt(set_value_at_thread_exit) member's
description;)
ittq(void set_value_at_thread_exit(ReturnType const &value))
(See the next tt(set_value_at_thread_exit) member's
description;)
ittq(void set_value_at_thread_exit(ReturnType &value))
(Stores tt(value) in the shared state without immediately making that
state ready. The state becomes ready when the current thread exits,
once all objects of thread storage duration which are associated with
the ending thread have been destroyed. A tt(future_error) exception
is thrown upon error, containing
itemization(
itt(promise_already_satisfied) if the shared state has already been
made ready;
itt(no_state) if the current object does not have any shared state;
))
ittq(void swap(promise& other) noexcept)
(The shared states (if any) of the current object and tt(other) are
exchanged.)
)
The following non-member (free) function operating on tt(promise) objects is
available:
itemization(
ittq(void swap(promise<ReturnType> &lhs, promise<ReturnType> &rhs)
noexcept)
(Calls tt(lhs.swap(rhs)))
)
|