asynchronous - sincronicas - TaskCompletionSource-Intentando entender el trabajo asincrónico sin hilos
significado de sincronica (2)
Estoy tratando de comprender el propósito de TaskCompletionSource
y su relación con el trabajo asincrónico / sin hilos. Creo que tengo una idea general, pero quiero asegurarme de que mi comprensión sea correcta.
Primero comencé a buscar en la Biblioteca de tareas paralelas (TPL) para descubrir si había una buena manera de crear su propio trabajo sin hilo / asincrónico (digamos que está tratando de mejorar la escalabilidad de su sitio ASP.NET) más la comprensión del TPL parece que será muy importante en el futuro ( async
/ await
). Lo que me llevó a TaskCompletionSource
.
Desde mi punto de vista, parece que agregar TaskCompletionSource
a una de tus clases no hace mucho para que la codificación sea asincrónica; si todavía está ejecutando el código de sincronización, la llamada a su código se bloqueará. Creo que esto es cierto incluso para las API de Microsoft. Por ejemplo, digamos en DownloadStringTaskAsync
fuera de la clase WebClient
, cualquier código de configuración / sincronización que estén haciendo inicialmente se bloqueará. El código que está ejecutando debe ejecutarse en algún subproceso, ya sea el actual o tendrá que derivar uno nuevo.
Por lo tanto, usa TaskCompletionSource
en su propio código cuando llama a otras llamadas async
de Microsoft para que el cliente de sus clases no tenga que crear un nuevo hilo para que su clase no se bloquee.
No estoy seguro de cómo Microsoft hace sus API asincrónicas internamente. Por ejemplo, hay un nuevo método async
fuera del SqlDataReader
para .Net 4.5. Sé que hay puertos de terminación IO. Creo que es una abstracción de nivel inferior (C ++?) Que probablemente la mayoría de los desarrolladores de C # no usarán. No estoy seguro de si los puertos de terminación IO funcionarán para las llamadas de red o de base de datos (HTTP) o si solo se utilizan para el archivo IO.
Entonces la pregunta es, ¿estoy en lo correcto en mi entendimiento correcto? ¿Hay ciertas cosas que he representado incorrectamente?
Me gusta la explicación que se proporcionó en http://tutorials.csharp-online.net/TaskCompletionSource
(Lo siento, el enlace puede estar muerto en este momento)
Los primeros dos párrafos están debajo
Hemos visto cómo Task.Run crea una tarea que ejecuta un delegado en un subproceso agrupado (o no agrupado). Otra forma de crear una tarea es con TaskCompletionSource.
TaskCompletionSource le permite crear una tarea de cualquier operación que se inicie y termine un tiempo después. Funciona proporcionándole una tarea "esclava" que maneja manualmente, indicando cuándo finaliza o falla la operación. Esto es ideal para el trabajo vinculado a E / S: obtiene todos los beneficios de las tareas (con su capacidad para propagar valores de retorno, excepciones y continuas) sin bloquear un hilo durante la operación.
Para usar TaskCompletionSource, simplemente crea una instancia de la clase. Expone una propiedad Tarea que devuelve una tarea en la que puede esperar y adjuntar continuaciones, al igual que con cualquier otra tarea. La tarea, sin embargo, está controlada completamente por el objeto TaskCompletionSource a través de los siguientes métodos:
public class TaskCompletionSource<TResult>
{
public void SetResult(TResult result);
public void SetException (Exception exception);
public void SetCanceled();
public bool TrySetResult (TResult result);
public bool TrySetException (Exception exception);
public bool TrySetCanceled();
...
}
Llamar a cualquiera de estos métodos señala la tarea, colocándola en un estado completado, fallado o cancelado (cubriremos este último en la sección "Cancelación"). Se supone que debes llamar a uno de estos métodos exactamente una vez: si se vuelve a llamar, SetResult, SetException o SetCanceled generarán una excepción, mientras que los métodos Try * devolverán false.
El siguiente ejemplo imprime 42 después de esperar cinco segundos:
var tcs = new TaskCompletionSource<int>();
new Thread (() => {
Thread.Sleep (5000);
tcs.SetResult (42);
})
.Start();
Task<int> task = tcs.Task; // Our "slave" task.
Console.WriteLine(task.Result); // 42
Otras citas interesantes
El poder real de TaskCompletionSource es crear tareas que no atan hilos.
.. y luego
Nuestro uso de TaskCompletionSource sin un hilo significa que un hilo se activa solo cuando se inicia la continuación, cinco segundos después. Podemos demostrar esto iniciando 10,000 de estas operaciones a la vez sin error o consumo excesivo de recursos:
TaskCompletionSource
se usa para crear objetos Task
que no ejecutan código.
Son utilizados bastante por las nuevas API asincrónicas de Microsoft, cada vez que hay operaciones asíncronas basadas en E / S (u otras operaciones asincrónicas no basadas en CPU, como un tiempo de espera). Además, cualquier método de async Task
que escriba usará TCS para completar su Task
devuelta.
Tengo una publicación de blog Creando tareas que analiza diferentes formas de crear instancias de Task
. Está escrito desde una perspectiva async
/ await
(no es una perspectiva de TPL), pero aún se aplica aquí.
También vea las excelentes publicaciones de Stephen Toub:
- La naturaleza de TaskCompletionSource
- Mecanismos para crear tareas
- espera algo; (usando
TaskCompletionSource
paraawait
cualquier cosa). - Usar tareas para implementar el patrón APM (crear
Begin
/End
usandoTaskCompletionSource
).