многопоточностьconcurrency
Асинхронный результат вычисления без ручной синхронизации через mutex и condition_variable. promise — сторона, которая пишет результат; future — сторона, которая его ждёт и читает; async — удобная обёртка, запускающая функцию и сразу возвращающая future.An asynchronous computation result without manual synchronization via mutex and condition_variable. promise is the side that writes the result; future is the side that waits for and reads it; async is a convenience wrapper that starts a function and immediately returns a future for it.
std::promise<T> и std::future<T> — парный одноразовый канал передачи результата (или исключения) между потоками: promise.set_value() на стороне производителя делает результат доступным, future.get() на стороне потребителя блокируется до появления результата и возвращает его ровно один раз. std::async — функция, которая запускает вызываемый объект (возможно, в отдельном потоке) и сразу возвращает future на его будущий результат.std::promise<T> and std::future<T> form a paired, single-use channel for passing a result (or an exception) between threads: promise.set_value() on the producer side makes the result available, future.get() on the consumer side blocks until the result appears and returns it exactly once. std::async is a function that launches a callable (possibly on a separate thread) and immediately returns a future for its eventual result.
int result; bool done = false;
std::mutex m; std::condition_variable cv;
std::thread t([&]{
result = compute();
{ std::lock_guard<std::mutex> lk(m); done = true; }
cv.notify_one();
});
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [&]{ return done; });
t.join();
std::future<int> fut = std::async(std::launch::async, compute);
int result = fut.get(); // блокируется до готовности,// blocks until ready,
// синхронизация — внутри future// synchronization lives inside future
future/promise/async закрывают частый шаблонный код поверх thread + mutex + condition_variable:future/promise/async eliminate a common piece of boilerplate built on top of thread + mutex + condition_variable:
Комбинация mutex + condition_variable + флаг готовности для передачи одного значения из одного потока в другой повторялась в коде настолько часто, что заслуживала стандартного, готового к использованию инструмента.The combination of mutex + condition_variable + a readiness flag for passing a single value from one thread to another showed up in code often enough to deserve a standard, ready-to-use tool.
Исключение, брошенное внутри потока, не может быть напрямую поймано вызывающим кодом в другом потоке — future.get() решает и эту задачу: если задача, связанная с promise, кинула исключение, оно ретранслируется в вызове get() на стороне потребителя.An exception thrown inside a thread can't be caught directly by calling code on another thread — future.get() solves this too: if the task tied to the promise threw, that exception is rethrown at the get() call on the consumer side.
std::async может выполнить функцию как в отдельном потоке (std::launch::async), так и отложенно, при первом обращении к future (std::launch::deferred) — вызывающий код работает с future одинаково в обоих случаях, не завязываясь на конкретный механизм.std::async can run a function either on a separate thread (std::launch::async) or lazily, on the first access to the future (std::launch::deferred) — calling code works with the future the same way in both cases, without depending on the specific mechanism.
Внутри promise/future лежит общее разделяемое состояние (shared state), доступ к которому синхронизирован — концептуально тем же mutex + condition_variable, которые разобраны в mutex-condvar.html и подробно — в cpp_concurrency.md. std::future/std::promise можно рассматривать как готовую, стандартизованную реализацию именно того паттерна «одноразовая передача результата», который иначе пришлось бы писать вручную.Underneath, promise/future share a common shared state, access to which is synchronized — conceptually the same mutex + condition_variable covered in mutex-condvar.html and in detail in cpp_concurrency.md. std::future/std::promise can be seen as a ready-made, standardized implementation of exactly that "single-use result handoff" pattern you'd otherwise have to write by hand.
#include <future>
int compute(int x) { return x * x; }
int main() {
// async — самый простой способ получить future:// async — the simplest way to get a future:
std::future<int> fut = std::async(std::launch::async, compute, 6);
// ... другая работа, пока compute считается в фоне ...// ... other work while compute runs in the background ...
int result = fut.get(); // блокируется, если ещё не готово; 36// blocks if not ready yet; 36
// promise — явный контроль над тем, кто и когда пишет результат:// promise — explicit control over who writes the result and when:
std::promise<int> prom;
std::future<int> fut2 = prom.get_future();
std::thread t([&prom] { prom.set_value(compute(7)); });
std::cout << fut2.get(); // 49
t.join();
}
future.get() можно вызвать только один раз — после этого future переходит в невалидное состояние; для повторного чтения результата несколькими потребителями в C++11 нет прямого решения (std::shared_future появляется тоже в C++11 именно для этого случая — множественные получатели одного результата).future.get() can only be called once — after that the future moves into an invalid state; C++11 has no direct solution for reading the result repeatedly from multiple consumers (std::shared_future, also introduced in C++11, exists precisely for that case — multiple recipients of the same result).
Вызов std::async(compute, x) без явной policy оставляет выбор между async и deferred на усмотрение реализации — если реализация выберет deferred, функция выполнится синхронно внутри вызова fut.get(), а не в фоне. Для гарантированного параллельного исполнения нужно явно указывать std::launch::async.Calling std::async(compute, x) without an explicit policy leaves the choice between async and deferred up to the implementation — if the implementation picks deferred, the function runs synchronously inside the fut.get() call rather than in the background. Guaranteed concurrent execution requires explicitly specifying std::launch::async.
В отличие от future, полученного из promise напрямую, future от std::async в некоторых реализациях блокируется в своём деструкторе до завершения задачи, если get()/wait() не были вызваны явно — неожиданная задержка в месте, которое выглядит как простое уничтожение объекта.Unlike a future obtained directly from a promise, a future from std::async, in some implementations, blocks in its destructor until the task finishes if get()/wait() weren't called explicitly — an unexpected delay at a spot that looks like a plain object destruction.
std::future рассчитан на одноразовое получение результата — второй вызов get() на том же объекте не определён стандартом (обычно кидает std::future_error). Для множественных читателей нужен std::shared_future, который допускает многократный get().std::future is designed for a single retrieval of the result — a second get() call on the same object is not defined by the standard (it typically throws std::future_error). Multiple readers need std::shared_future, which allows calling get() repeatedly.