2012-10-11 22 views
11

Una forma de obtener una std::future es a través de std::async:¿Cómo se asigna el almacenamiento asociado con std :: future?

int foo() 
{ 
    return 42; 
} 

... 

std::future<int> x = std::async(foo); 

En este ejemplo, cómo es el almacenamiento para x 's de estado asíncrona asignado, y por cuál de ellos (si hay más de un hilo está implicado) es responsable de realizar la asignación? Además, ¿un cliente de std::async tiene control sobre la asignación?

Para el contexto, veo que one of the constructors de std::promise puede recibir un asignador, pero no es claro para mí si es posible personalizar la asignación de la std::future a nivel de std::async.

Respuesta

5

A juzgar por los simples argumentos de std::async, parece que no hay forma de controlar la asignación del std::promise interno y, por lo tanto, puede simplemente usar cualquier cosa, aunque probablemente std::allocator. Aunque supongo que en teoría no se especifica, es probable que el estado compartido se asigne dentro del hilo de llamada. No he encontrado ninguna información explícita en el estándar sobre este asunto. Al final, std::async es una función muy especializada para una invocación asíncrona fácil, por lo que no tiene que pensar si realmente es a std::promise en cualquier lugar.

Para obtener un control más directo sobre el comportamiento de una llamada asíncrona, también existe std::packaged_task, que de hecho tiene un argumento de asignación. Pero a partir de la mera cita estándar no está perfectamente claro si este asignador solo se usa para asignar almacenamiento para la función (ya que std::packaged_task es un tipo especial std::function) o si también se usa para asignar el estado compartido del std::promise interno, aunque parece probable:

30.6.9.1 [futures.task.members]:

Efectos: construye un nuevo packaged_task objeto con un estado compartido y inicializa tarea almacenada en el objeto con std::forward<F>(f). Los constructores que toman un argumento Allocator lo usan para asignar la memoria necesaria para almacenar las estructuras internas de datos.

Bueno, ni siquiera decir que no es un std::promise debajo (lo mismo para std::async), podría ser sólo un tipo indefinido puede conectar a una std::future.

Por lo tanto, si de hecho no se especifica cómo std::packaged_task asigna su estado interno compartido, su mejor opción podría ser implementar sus propias instalaciones para la invocación de funciones asincrónicas. Teniendo en cuenta que, simplemente hablado, un std::packaged_task es solo un std::function incluido con un std::promise y std::async acaba de iniciar un std::packaged_task en un nuevo hilo (bueno, excepto cuando no lo hace), esto no debería ser un gran problema.

Pero de hecho esto podría ser un descuido en la especificación. Mientras que el control de asignación realmente no se ajusta al std::async, la explicación de std::packaged_task y su uso de asignadores podría ser un poco más clara. Pero esto también puede ser intencional, por lo que el std::packaged_task es libre de usar lo que quiera y ni siquiera necesita un std::promise internamente.

EDIT: lectura de nuevo, creo que la cotización estándar por encima de hecho dice, que los std::packaged_task 's estado compartido se asignado usando el asignador proporcionado, ya que es parte de los 'estructuras de datos internas' , cualquiera que sean (sin embargo, no es necesario que sea realmente std::promise). Así que creo que std::packaged_task debería ser suficiente para tener un control explícito del estado compartido de una tarea asíncrona std::future.

5

La memoria está asignada por el hilo que llama al std::async, y usted no tiene control sobre cómo se hace. Por lo general, se realizará mediante alguna variante de new __internal_state_type, pero no hay garantía; puede usar malloc, o un asignador específicamente elegido para este propósito.

De 30.6.8p3 [futures.async]:

"Efectos: La primera función se comporta igual que una llamada a la segunda función con un argumento de política de launch::async | launch::deferred y los mismos argumentos a favor y FArgs La segunda función crea un estado compartido que está asociado con el objeto futuro devuelto ... "

La" primera función "es la sobrecarga sin una política de inicio, mientras que la segunda es la sobrecarga con una política de inicio .

En el caso de std::launch::deferred, no hay otro hilo, por lo que todo debe suceder en el hilo de llamada. En el caso de std::launch::async, 30.6.8p3 continúa diciendo:

- si policy & launch::async no es cero - llama INVOKE (DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) (20.8.2, 30.3.1.2) como en un nuevo hilo de ejecución representado por una enhebrar el objeto con las llamadas a DECAY_COPY() siendo evaluadas en la secuencia que llamó a async. ...

He añadido el énfasis. Como la copia de la función y los argumentos tiene que ocurrir en el hilo de llamada, esto esencialmente requiere que el estado compartido sea asignado por el hilo de llamada.

Por supuesto, podría escribir una implementación que inició el nuevo hilo, esperó a que asignara el estado, y luego devolvió un future que hacía referencia a eso, pero ¿por qué lo haría?

+0

¿Dónde se garantiza que el subproceso de llamada asigna el almacenamiento? Ok, es bastante probable, pero aún me gustaría ver una cita del estándar. –

+0

Respuesta actualizada con más detalles. –

+0

De hecho, parece bastante claro, pasé por alto eso. Pero señalar esto en su respuesta como lo hace ahora no puede hacer ningún daño. Gracias y +1. –

Cuestiones relacionadas