Tarea.Ejecutar con Parámetro(s)?


Estoy trabajando en un proyecto de red multitarea y soy nuevo en Threading.Tasks. He implementado un simple Task.Factory.StartNew() y me pregunto ¿cómo puedo hacerlo con Task.Run()?

Aquí está el código básico:

Task.Factory.StartNew(new Action<object>(
(x) =>
{
    // Do something with 'x'
}), rawData);

Miré en System.Threading.Tasks.Tasken Explorador de objetos y no pude encontrar un parámetro similar a Action<T>. Solo hay Action que toma void parámetro y no tipo.

Solo hay 2 cosas similares: static Task Run(Action action) y static Task Run(Func<Task> function) pero no se pueden publicar parámetros con ambos.

Sí, lo sé Puedo crear un método de extensión simple para él, pero mi pregunta principal es ¿podemos escribirlo en una sola línea con Task.Run()?

Author: MFatihMAR, 2015-05-14

5 answers

private void RunAsync()
{
    string param = "Hi";
    Task.Run(() => MethodWithParameter(param));
}

private void MethodWithParameter(string param)
{
    //Do stuff
}

Editar

Debido a la demanda popular debo tener en cuenta que el Task lanzado se ejecutará en paralelo con el hilo de llamada. Asumiendo el valor predeterminado TaskScheduler esto utilizará el.NET ThreadPool. De todos modos, esto significa que necesita tener en cuenta cualquier parámetro(s) que se pasa a la Task como potencialmente se accede por múltiples hilos a la vez, haciéndolos estado compartido. Esto incluye acceder a ellos en el hilo de llamada.

En mi código anterior ese caso se hace enteramente discutible. Las cadenas son inmutables. Por eso los usé como ejemplo. Pero digamos que no estás usando un String...

Una solución es usar async y await. Esto, por defecto, capturará el SynchronizationContext del hilo de llamada y creará una continuación para el resto del método después de la llamada a await y lo adjuntará al Task creado. Si este método se está ejecutando en el subproceso de la GUI de WinForms, será de tipo WindowsFormsSynchronizationContext.

La continuación se ejecutará después de ser publicado de nuevo a la captured SynchronizationContext - de nuevo solo por defecto. Así que volverás al hilo con el que empezaste después de la llamada await. Puede cambiar esto de varias maneras, especialmente usando ConfigureAwait. En resumen, el resto de ese método no continuará hasta después de que el Task se haya completado en otro hilo. Pero el hilo de llamada continuará ejecutándose en paralelo, solo que no el resto del método.

Esta espera para completar la ejecución del resto del método puede o no ser deseable. Si nada en ese método accede más tarde a los parámetros pasados a Task es posible que no desee usar await en absoluto.

O tal vez use esos parámetros mucho más adelante en el método. No hay razón para await inmediatamente ya que podría continuar con seguridad haciendo el trabajo. Recuerde, puede almacenar el Task devuelto en una variable y await en él más tarde, incluso en el mismo método. Por ejemplo, una vez que necesite acceder a los parámetros pasados de forma segura después de hacer un montón de otro trabajo. Una vez más, lo haces no necesita await en el Task justo cuando lo ejecuta.

De todos modos, una forma sencilla de hacer este hilo seguro con respecto a los parámetros pasados a Task.Run es hacer esto:

Primero debes decorar RunAsync con async:

private async void RunAsync()

Nota Importante

Preferiblemente el método marcado async no debería devolver void, como menciona la documentación enlazada. La excepción común a esto son los controladores de eventos, como los clics de botón y tal. Deben regresar vacíos. De lo contrario, siempre intento devolver un Task o Task<TResult> cuando uso async. Es una buena práctica por varias razones.

Ahora puedes await ejecutar el Task como a continuación. No se puede usar await sin async.

await Task.Run(() => MethodWithParameter(param));
//Code here and below in the same method will not run until AFTER the above task has completed in one fashion or another

Entonces, en general, si await la tarea puede evitar tratar los parámetros pasados como un recurso potencialmente compartido con todas las trampas de modificar algo de varios subprocesos a la vez. También, cuidado concierres . No los cubriré en profundidad, pero el artículo vinculado hace un gran trabajo.

Nota al margen

Un poco fuera de tema, pero tenga cuidado de usar cualquier tipo de "bloqueo" en el hilo de la GUI de WinForms debido a que está marcado con [STAThread]. Usar await no bloqueará en absoluto, pero a veces veo que se usa junto con algún tipo de bloqueo.

"Bloquear" está entre comillas porque técnicamente no puede bloquear el hilo de la GUI de WinForms. Sí, si utiliza lock en el WinForms GUI thread it seguirá bombeando mensajes, a pesar de que pienses que está "bloqueado". No lo es.

Esto puede causar problemas extraños en casos muy raros. Una de las razones por las que nunca quieres usar un lock al pintar, por ejemplo. Pero ese es un caso marginal y complejo; sin embargo, he visto que causa problemas locos. Así que lo anoté por completo.

 61
Author: Zer0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-18 01:44:18

Utilice la captura de variables para "pasar" parámetros.

var x = rawData;
Task.Run(() =>
{
    // Do something with 'x'
});

También puede usar rawData directamente, pero debe tener cuidado, si cambia el valor de rawData fuera de una tarea (por ejemplo, un iterador en un bucle for) también cambiará el valor dentro de la tarea.

 18
Author: Scott Chamberlain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-13 21:33:40

Simplemente use Task.Run

var task = Task.Run(() =>
{
    //this will already share scope with rawData, no need to use a placeholder
});

O, si desea usarlo en un método y esperar la tarea más tarde

public Task<T> SomethingAsync<T>()
{
    var task = Task.Run(() =>
    {
        //presumably do something which takes a few ms here
        //this will share scope with any passed parameters in the method
        return default(T);
    });

    return task;
}
 2
Author: Travis J,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-13 21:40:00

Sé que este es un hilo viejo, pero quería compartir una solución que terminé teniendo que usar ya que el mensaje aceptado todavía tiene un problema.

La cuestión:

Como señaló Alexandre Severino, si param (en la función de abajo) cambia poco después de la llamada a la función, puede obtener algún comportamiento inesperado en MethodWithParameter.

Task.Run(() => MethodWithParameter(param)); 

Mi solución:

Para explicar esto, terminé escribiendo algo más como la siguiente línea de código:

(new Func<T, Task>(async (p) => await Task.Run(() => MethodWithParam(p)))).Invoke(param);

Esto me permitió para utilizar de forma segura el parámetro de forma asíncrona a pesar del hecho de que el parámetro cambió muy rápidamente después de iniciar la tarea (lo que causó problemas con la solución publicada).

Usando este enfoque, param (tipo de valor) obtiene su valor pasado, por lo que incluso si el método async se ejecuta después de que param cambie, p tendrá cualquier valor que param tuviera cuando se ejecutó esta línea de código.

 2
Author: Kaden Burgart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-13 20:04:25

A partir de ahora también puedes :

Action<int> action = (o) => Thread.Sleep(o);
int param = 10;
await new TaskFactory().StartNew(action, param)
 1
Author: Arnaud F.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-08 20:53:58