c++ - comportamiento de std:: async(std:: launch:: aplazado)+std:: future:: then
concurrency c++17 (1)
Esto, en mi opinión, parece ser un error en el TS . O al menos una trampa poco documentada.
Aquí está el texto del TS:
2.3 [futures.unique_future] / 6-10
template <class F> see below then(F&& func);
Requiere:
INVOKE(DECAY_COPY (std::forward<F>(func)), std::move(*this)) shall be a valid expression.
Efectos:
La función crea un estado compartido que está asociado con el objeto futuro devuelto. Adicionalmente,
Cuando el estado compartido del objeto está listo, se
INVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this))
la continuaciónINVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this))
en un subproceso de ejecución no especificado con la llamada aDECAY_COPY()
siendo evaluado en el hilo que llamaba entonces.Cualquier valor devuelto de la continuación se almacena como resultado en el estado compartido del futuro resultante. Cualquier excepción propagada por la ejecución de la continuación se almacena como el resultado excepcional en el estado compartido del futuro resultante.
Devoluciones:
Cuando
result_of_t<decay_t<F>(future<R>)>
esfuture<R2>
, para algún tipo de R2, la función devuelvefuture<R2>
. De lo contrario, la función devuelvefuture<result_of_t<decay_t<F>(future<R>)>>
. [Nota: la regla anterior se denomina desenvolvimiento implícito. Sin esta regla, el tipo de retorno de tomar un retornablefuture<R>
habría sidofuture<future<R>>
. Esta regla evita tales objetos futuros anidados. El tipo def2
continuación es elfuture<int>
y no elfuture<future<int>>
:[Ejemplo:
future<int> f1 = g(); future<int> f2 = f1.then([](future<int> f) { future<int> f3 = h(); return f3; });
- ejemplo final]
- nota final]
Postcondiciones:
valid() == false
en el futuro original.valid() == true
en el futuro devuelto desde entonces. [Nota: En el caso de que se haya desempaquetado implícito, la validez del futuro devuelto desde thenfunc no se puede establecer hasta después de la finalización de la continuación. Si no es válido, el futuro resultante estará listo con una excepción de tipostd::future_error
, con una condición de error destd::future_errc::broken_promise
. - nota final]
No hay ningún caso especial para una tarea futura diferida. Si esa tarea futura diferida no está lista antes de llamar a .then
, no hay forma de que esté lista, por lo que no hay forma de invocar la copia descompuesta de la func
.
El texto para shared_future
es similar; allí, sin embargo, puede hacer que el shared_future
esté listo después de llamar .then
.
Si se pretende esto; que, .then
en un futuro único diferido no listo, resultará en un valor de retorno de un future
que nunca se puede preparar, esto debería estar explícito en la norma TS /. Si esto no se pretende, el texto estándar debe ser cambiado.
Tenga en cuenta que estos cambios no aparecen en el borrador de norma N4762 publicado en 2018.
No estoy seguro de cómo el estándar debería arreglar esto; la semántica es razonable para un shared_future
pero no para un future
, y una semántica diferente sería sorprendente.
La idea detrás de un futuro diferido (que solo se logra llamando a std::async
con std::launch::deferred
flag) es que la devolución de llamada se realiza solo cuando alguien intenta esperar o extraer el valor futurista o la excepción del futuro. para entonces la devolución de llamada no se ejecuta.
¿Qué sucede si adjunto una continuación a un futuro diferido con std::future::then
? el futuro diferido se pierde (y then
invalida el futuro) y en su lugar se devuelve un nuevo futuro.
En este caso, según la norma, ¿qué debería pasar? ¿El nuevo futuro es también un futuro diferido? ¿Sería un punto muerto? Esta pregunta no se aborda en la documentación más reciente.