thread create c++ multithreading c++11 future stdthread

c++ - create - Confusión sobre los hilos lanzados por std:: async con el parámetro std:: launch:: async



create thread c++ (3)

Eso no es realmente cierto. Agregue el valor almacenado thread_local y verá que en realidad std::async run f1 f2 f3 tareas en diferentes subprocesos, pero con el mismo std::thread::id

Estoy un poco confundido acerca de la función std::async .

La especificación dice: la operación asíncrona se está ejecutando "como en un nuevo hilo de ejecución" (C ++ 11 §30.6.8 / 11).

Ahora, ¿qué se supone que significa eso?

En mi entendimiento, el código

std::future<double> fut = std::async(std::launch::async, pow2, num);

debe iniciar la función pow2 en un nuevo hilo y pasar la variable num al hilo por valor, y luego, en algún momento en el futuro, cuando la función haya terminado, coloque el resultado en fut (siempre que la función pow2 tenga una firma como double pow2(double); ). Pero la especificación dice "como si", lo que hace que todo sea un poco confuso para mí.

La pregunta es:

¿Se lanza siempre un nuevo hilo en este caso? Yo espero que sí. En mi opinión, el parámetro std::launch::async tiene sentido de una manera en la que estoy declarando explícitamente que deseo crear un nuevo hilo.

Y el codigo

std::future<double> fut = std::async(std::launch::deferred, pow2, num);

debería hacer posible la evaluación perezosa , retrasando la llamada de la función pow2 al punto donde escribo algo como var = fut.get(); . En este caso, el parámetro std::launch::deferred debería significar que estoy declarando explícitamente que no quiero un nuevo hilo, solo quiero asegurarme de que se llame a la función cuando sea necesario para su valor de retorno.

¿Mis suposiciones son correctas? Si no, por favor explique.

Además, sé que, por defecto, la función se llama de la siguiente manera:

std::future<double> fut = std::async(std::launch::deferred | std::launch::async, pow2, num);

En este caso, me dijeron que si un nuevo hilo se lanzará o no depende de la implementación. De nuevo, ¿qué se supone que significa eso?


Esto también me confundió y ejecuté una prueba rápida en Windows que muestra que el futuro asíncrono se ejecutará en los subprocesos del grupo de subprocesos del sistema operativo. Una aplicación simple puede demostrar esto, al descomponerse en Visual Studio también se mostrarán los subprocesos en ejecución llamados "TppWorkerThread".

#include <future> #include <thread> #include <iostream> using namespace std; int main() { cout << "main thread id " << this_thread::get_id() << endl; future<int> f1 = async(launch::async, [](){ cout << "future run on thread " << this_thread::get_id() << endl; return 1; }); f1.get(); future<int> f2 = async(launch::async, [](){ cout << "future run on thread " << this_thread::get_id() << endl; return 1; }); f2.get(); future<int> f3 = async(launch::async, [](){ cout << "future run on thread " << this_thread::get_id() << endl; return 1; }); f3.get(); cin.ignore(); return 0; }

Resultará en una salida similar a:

main thread id 4164 future run on thread 4188 future run on thread 4188 future run on thread 4188


La plantilla de función std::async (parte de la cabecera <future> ) se utiliza para iniciar una tarea asíncrona (posiblemente). Devuelve un objeto std::future , que eventualmente retendrá el valor de retorno de la función de parámetros de std::async .

Cuando se necesita el valor, llamamos a get () en la instancia std::future ; esto bloquea el hilo hasta que el futuro esté listo y luego devuelve el valor. std::launch::async o std::launch::deferred se puede especificar como el primer parámetro de std::async para especificar cómo se ejecuta la tarea.

  1. std::launch::async indica que la llamada a la función debe ejecutarse en su propio (nuevo) hilo. (Tome en cuenta el comentario del usuario @ TC).
  2. std::launch::deferred indica que la llamada a la función se aplazará hasta que se llame a wait() o get() en el futuro. La propiedad del futuro se puede transferir a otro hilo antes de que esto suceda.
  3. std::launch::async | std::launch::deferred std::launch::async | std::launch::deferred indica que la implementación puede elegir. Esta es la opción predeterminada (cuando no especifica uno por sí mismo). Puede decidir ejecutarse sincrónicamente.

¿Se lanza siempre un nuevo hilo en este caso?

Desde 1. podemos decir que siempre se lanza un nuevo hilo.

¿Son correctas mis suposiciones [en std :: launch :: deferred]?

A partir del 2. , podemos decir que sus suposiciones son correctas.

¿Qué se supone que significa eso? [en relación con un nuevo hilo que se está lanzando o no dependiendo de la implementación]

Desde 3. , como std::launch::async | std::launch::deferred std::launch::async | std::launch::deferred es la opción por defecto, significa que la implementación de la función de plantilla std::async decidirá si creará un nuevo hilo o no. Esto se debe a que algunas implementaciones pueden estar comprobando la programación excesiva.

ADVERTENCIA

La siguiente sección no está relacionada con su pregunta, pero creo que es importante tener en cuenta.

El estándar de C ++ dice que si un std::future contiene la última referencia al estado compartido correspondiente a una llamada a una función asíncrona, el destructor de std :: future debe bloquearse hasta que finalice el subproceso de la función en ejecución asíncrona. Una instancia de std::future devuelta por std::async se bloqueará en su destructor.

void operation() { auto func = [] { std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); }; std::async( std::launch::async, func ); std::async( std::launch::async, func ); std::future<void> f{ std::async( std::launch::async, func ) }; }

Este código engañoso puede hacerte pensar que las llamadas std::async son asíncronas, en realidad son síncronas. Las instancias std::future devueltas por std::async son temporales y se bloquearán porque su destructor se llama justo cuando std::async regresa ya que no están asignadas a una variable.

La primera llamada a std::async se bloqueará durante 2 segundos, seguidos de otros 2 segundos de bloqueo desde la segunda llamada a std::async . Podemos pensar que la última llamada a std::async no se bloquea, ya que almacenamos su instancia std::future devuelta en una variable, pero como esa es una variable local que se destruye al final del alcance, en realidad bloque durante 2 segundos adicionales al final del alcance de la función, cuando se destruye la variable local f.

En otras palabras, llamar a la función operation() bloqueará cualquier subproceso en el que se llame de forma síncrona durante aproximadamente 6 segundos. Es posible que tales requisitos no existan en una versión futura del estándar C ++.

Fuentes de información que utilicé para compilar estas notas:

C ++ Concurrencia en Acción: Multihilo práctico, Anthony Williams

Publicación del blog de Scott Meyers: http://scottmeyers.blogspot.ca/2013/03/stdfutures-from-stdasync-arent-special.html