std::async
Definido en el archivo de encabezado <future> | ||
(1) | ||
template<class Function, class... Args> std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> | (desde C++11) (hasta C++17) | |
template<class Function, class... Args> std::future<std::invoke_result_t<std::decay_t<Function>, | (desde C++17) (hasta C++20) | |
template<class Function, class... Args> [[nodiscard]] | (desde C++20) | |
(2) | ||
template<class Function, class... Args> std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> | (desde C++11) (hasta C++17) | |
template<class Function, class... Args> std::future<std::invoke_result_t<std::decay_t<Function>, | (desde C++17) (hasta C++20) | |
template<class Function, class... Args> [[nodiscard]] | (desde C++20) | |
La plantilla de función async
ejecuta la función f
asíncronamente (potencialmente en un hilo separado que puede ser parte de una reserva de hilos) y devuelve un std::future que eventualmente contendrá el resultado de esa llamada a función.
f
puede ejecutarse en otro hilo o puede ejecutarse asíncronamente cuando el std::future resultante es consultado por un valor.f
con los argumentos args
de acuerdo con una política de lanzamiento específica policy
: - Si la bandera async se establece (es decir, policy &std::launch::async!=0), entonces
async
ejecuta el objeto llamablef
en un nuevo hilo de ejecución (con todas las variables locales al hilo inicializadas) como si se hubieran generado por std::thread(std::forward<F>(f), std::forward<Args>(args)...), excepto que si la funciónf
devuelve un valor o produce una excepción, éste se almacena en el estado compartido accesible a través del std::future queasync
devuelve al llamador. - Si la bandera deferred se establece (es decir, policy &std::launch::deferred!=0), entonces
async
convierte af
y aargs...
de la misma manera que si lo fuera mediante el constructor de std::thread, pero no genera un nuevo hilo de ejecución. En su lugar se realiza evaluación perezosa: la primera llamada a una función de espera sin sincronizar en el std::future que devolvióasync
al llamador ocasionará que se invoque a la copia def
(como un rvalue) con las copias deargs...
(también pasados como rvalues) en el hilo actual (que no tiene que ser el hilo que llamó originalmente astd::async
). El resultado o excepción se ubica en el estado compartido asociado con el std::future y solamente entonces se deja listo. Todos los accesos posteriores al mismo std::future devolverán el resultado inmediatamente.
- Si tanto la bandera std::launch::async como la bandera std::launch::deferred están establecidas en
policy
, depende de la implementación si se realiza una ejecución asíncrona o una evaluación perezosa.
- Si tanto la bandera std::launch::async como la bandera std::launch::deferred están establecidas en
| (desde C++14) |
En cualquier caso, la llamada a std::async
se sincroniza-con (como se define en el orden de memoria (std::memory_order)) la llamada a f
, y la finalización de f
es secuenciada-antes de dejar el estado compartido listo. Si se escoge la política async
, la finalización del hilo asociado se sincroniza-con el retorno exitoso de la primera función que está esperando al estado compartido, o con el retorno de la última función que libera el estado compartido, lo que suceda primero.
Contenido |
[editar]Parámetros
f | - | Objeto que puede ser llamado (Callable) a llamar. | ||||||
args... | - | Parámetros para pasar a f . | ||||||
policy | - | Valor de máscara de bits, donde los bits individuales controlan los métodos de ejecución permitidos.
| ||||||
Requisitos de tipo | ||||||||
-Function, Args debe satisfacer los requisitos de MoveConstructible. |
[editar]Valor de retorno
Un futuro (std::future) que se refiere al estado compartido creado por esta llamada a std::async
.
[editar]Excepciones
Lanza std::system_error con la condición de error std::errc::resource_unavailable_try_again si la política de lanzamiento es igual a std::launch::async y la implementación no es capaz de iniciar un nuevo hilo (si la política es async|deferred
o tiene bits adicionales establecidos, volverá a deferred o a las políticas definidas por la implementación, en este caso), o std::bad_alloc si no se puede asignar memoria para las estructuras de datos internas.
[editar]Notas
La implementación puede extender el comportamiento de la primera sobrecarga de std::async al habilitar bits adicionales (definidos por la implementación) en la política de inicio predeterminada.
Ejemplos de políticas de inicio definidas por la implementación son la política de sincronización (ejecutar inmediatamente, dentro de la llamada asíncrona) y la política de tareas (similar a la asíncrona, pero las variables locales al hilo no se borran).
Si el futuro (std::future
) obtenido de std::async
no se mueve o se vincula a una referencia, el destructor del std::future se bloqueará al final de la expresión completa hasta que se complete la operación asíncrona, esencialmente haciendo que código como el siguiente sea síncrono:
std::async(std::launch::async, []{ f();});// dtor del temporal espera a f() std::async(std::launch::async, []{ g();});// no comienza hasta que se complete f()
(ten en cuenta que los destructores de futuros std::future
obtenidos por medios distintos de una llamada a std::async
nunca se bloquean).
[editar]Ejemplo
#include <iostream>#include <vector>#include <algorithm>#include <numeric>#include <future>#include <string>#include <mutex> std::mutex m;struct X {void foo(int i, conststd::string& str){std::lock_guard<std::mutex> lk(m);std::cout<< str <<' '<< i <<'\n';}void bar(conststd::string& str){std::lock_guard<std::mutex> lk(m);std::cout<< str <<'\n';}int operator()(int i){std::lock_guard<std::mutex> lk(m);std::cout<< i <<'\n';return i +10;}}; template<typename RandomIt>int parallel_sum(RandomIt beg, RandomIt end){auto len = end - beg;if(len <1000)returnstd::accumulate(beg, end, 0); RandomIt mid = beg + len/2;auto handle = std::async(std::launch::async, parallel_sum<RandomIt>, mid, end);int sum = parallel_sum(beg, mid);return sum + handle.get();} int main(){std::vector<int> v(10000, 1);std::cout<<"La suma es "<< parallel_sum(v.begin(), v.end())<<'\n'; X x;// Llama a (&x)->foo(42, "Hola") con la política por defecto:// puede imprimir "Hola 42" concurrentemente o diferir la ejecuciónauto a1 = std::async(&X::foo, &x, 42, "Hola"); // Llama a x.bar("mundo") con la política diferida// imprime "mundo" cuando se llama a a2.get() o a a2.wait()auto a2 = std::async(std::launch::deferred, &X::bar, x, "mundo"); // Llama a X()(43); con política asíncrona (async)// imprime "43" concurrentementeauto a3 = std::async(std::launch::async, X(), 43); a2.wait();// imprime "mundo"std::cout<< a3.get()<<'\n';// imprime "53" }// si a1 no ha terminado en este punto, el destructor de a1 aquí imprime "Hola 42"
Posible salida:
La suma es 10000 43 mundo 53 Hola 42
[editar] Informes de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
LWG 2021 | C++11 | Tipo de retorno incorrecto y categoría de valor de los argumentos no son claros en el caso diferido | Tipo de retorno corregido y se clarificó que se utilizan rvalues. |