Pensé que eran básicamente lo mismo: escribir programas que dividen las tareas entre procesadores (en máquinas que tienen 2+ procesadores). Entonces estoy leyendo esto , que dice:
Los métodos asíncronos están destinados a ser operaciones sin bloqueo. Una expresión de espera en un método asíncrono no bloquea el hilo actual mientras se ejecuta la tarea esperada. En cambio, la expresión registra el resto del método como una continuación y devuelve el control al llamador del método asíncrono.
Las palabras clave asíncronas y en espera no hacen que se creen hilos adicionales. Los métodos asincrónicos no requieren subprocesos múltiples porque un método asincrónico no se ejecuta en su propio hilo. El método se ejecuta en el contexto de sincronización actual y usa el tiempo en el hilo solo cuando el método está activo. Puede usar Task.Run para mover el trabajo vinculado a la CPU a un subproceso en segundo plano, pero un subproceso en segundo plano no ayuda con un proceso que solo está esperando que los resultados estén disponibles.
y me pregunto si alguien puede traducir eso al inglés para mí. Parece establecer una distinción entre asincronicidad (¿es eso una palabra?) Y subprocesos e implica que puede tener un programa que tiene tareas asincrónicas pero no subprocesamiento múltiple.
Ahora entiendo la idea de tareas asincrónicas como el ejemplo en la pág. 467 de C # In Jon Deke Skeet en profundidad, tercera edición
async void DisplayWebsiteLength ( object sender, EventArgs e )
{
label.Text = "Fetching ...";
using ( HttpClient client = new HttpClient() )
{
Task<string> task = client.GetStringAsync("http://csharpindepth.com");
string text = await task;
label.Text = text.Length.ToString();
}
}
La async
palabra clave significa " Esta función, siempre que se llame, no se llamará en un contexto en el que se requiera su finalización para todo después de que se llame su llamada".
En otras palabras, escribirlo en medio de alguna tarea
int x = 5;
DisplayWebsiteLength();
double y = Math.Pow((double)x,2000.0);
, ya que DisplayWebsiteLength()
no tiene nada que ver con x
o y
, hará DisplayWebsiteLength()
que se ejecute "en segundo plano", como
processor 1 | processor 2
-------------------------------------------------------------------
int x = 5; | DisplayWebsiteLength()
double y = Math.Pow((double)x,2000.0); |
Obviamente ese es un ejemplo estúpido, pero ¿estoy en lo correcto o estoy totalmente confundido o qué?
(Además, estoy confundido acerca de por qué sender
y e
nunca se usan en el cuerpo de la función anterior).
fuente
sender
ye
estamos sugiriendo que este es en realidad un controlador de eventos, prácticamente el único lugar dondeasync void
es deseable. Lo más probable es que esto se llame al hacer clic en un botón o algo así; el resultado es que esta acción ocurre de forma completamente asincrónica con respecto al resto de la aplicación. Pero todavía está todo en un hilo: el hilo de la interfaz de usuario (con un pequeño lapso de tiempo en un hilo de IOCP que publica la devolución de llamada en el hilo de la interfaz de usuario).DisplayWebsiteLength
ejemplo de código: no debe usarseHttpClient
en unausing
declaración: bajo una carga pesada, el código puede agotar la cantidad de sockets disponibles, lo que resulta en errores de SocketException. Más información sobre instanciación incorrecta .Respuestas:
Tu malentendido es extremadamente común. A muchas personas se les enseña que los subprocesos múltiples y la asincronía son lo mismo, pero no lo son.
Una analogía generalmente ayuda. Estás cocinando en un restaurante. Entra una orden de huevos y tostadas.
¿Tiene sentido ahora que el subprocesamiento múltiple es solo un tipo de asincronía? Enhebrar se trata de trabajadores; La asincronía se trata de tareas . En los flujos de trabajo multiproceso, asigna tareas a los trabajadores. En los flujos de trabajo asincrónicos de subproceso único, tiene un gráfico de tareas donde algunas tareas dependen de los resultados de otras; a medida que cada tarea se completa, invoca el código que programa la próxima tarea que puede ejecutarse, dados los resultados de la tarea que se acaba de completar. Pero (con suerte) solo necesita un trabajador para realizar todas las tareas, no un trabajador por tarea.
Ayudará a darse cuenta de que muchas tareas no están vinculadas al procesador. Para las tareas vinculadas al procesador, tiene sentido contratar tantos trabajadores (subprocesos) como procesadores, asignar una tarea a cada trabajador, asignar un procesador a cada trabajador y hacer que cada procesador haga el trabajo de nada más que calcular el resultado como lo mas rapido posible. Pero para las tareas que no están esperando en un procesador, no necesita asignar un trabajador en absoluto. Usted sólo tiene que esperar para que el mensaje llegue de que el resultado está disponible y hacer otra cosa mientras espera . Cuando llegue ese mensaje, puede programar la continuación de la tarea completada como lo siguiente en su lista de tareas pendientes para marcar.
Así que veamos el ejemplo de Jon con más detalle. ¿Lo que pasa?
text
y ejecutar el resto del método.Es como en mi analogía. Alguien te pide un documento. Envías por correo el documento y sigues haciendo otro trabajo. Cuando llegue por correo, se le indicará, y cuando lo desee, hará el resto del flujo de trabajo: abra el sobre, pague los gastos de envío, lo que sea. No necesita contratar a otro trabajador para que haga todo eso por usted.
fuente
Javascript en el navegador es un gran ejemplo de un programa asincrónico que no tiene hilos.
No tiene que preocuparse de que varias partes del código toquen los mismos objetos al mismo tiempo: cada función terminará de ejecutarse antes de que cualquier otro javascript pueda ejecutarse en la página.
Sin embargo, al hacer algo como una solicitud AJAX, no se está ejecutando ningún código, por lo que otros javascript pueden responder a cosas como eventos de clic hasta que esa solicitud regrese e invoque la devolución de llamada asociada con ella. Si uno de estos otros controladores de eventos todavía se está ejecutando cuando vuelve la solicitud AJAX, no se llamará a su controlador hasta que finalicen. Solo se está ejecutando un "hilo" de JavaScript, aunque es posible que pause efectivamente lo que estaba haciendo hasta que tenga la información que necesita.
En las aplicaciones de C #, ocurre lo mismo cada vez que se trata de elementos de la interfaz de usuario: solo se le permite interactuar con los elementos de la interfaz de usuario cuando está en el hilo de la interfaz de usuario. Si el usuario hizo clic en un botón y usted quería responder leyendo un archivo grande del disco, un programador sin experiencia podría cometer el error de leer el archivo dentro del controlador de eventos de clic, lo que haría que la aplicación se "congelara" hasta que el archivo terminó de cargarse porque no está permitido responder a más clics, desplazamientos o cualquier otro evento relacionado con la interfaz de usuario hasta que se libere ese hilo.
Una opción que los programadores podrían usar para evitar este problema es crear un nuevo hilo para cargar el archivo, y luego decirle al código de ese hilo que cuando se carga el archivo necesita ejecutar el código restante en el hilo de la interfaz de usuario nuevamente para que pueda actualizar los elementos de la interfaz de usuario basado en lo que encontró en el archivo. Hasta hace poco, este enfoque era muy popular porque era lo que las bibliotecas y el lenguaje de C # facilitaban, pero es fundamentalmente más complicado de lo que tiene que ser.
Si piensa en lo que hace la CPU cuando lee un archivo a nivel del hardware y el sistema operativo, básicamente emite una instrucción para leer datos del disco en la memoria y golpear el sistema operativo con una "interrupción "cuando se completa la lectura. En otras palabras, leer desde el disco (o cualquier E / S realmente) es una operación inherentemente asíncrona . El concepto de un subproceso que espera que se complete esa E / S es una abstracción que los desarrolladores de la biblioteca crearon para facilitar la programación. No es necesario.
Ahora, la mayoría de las operaciones de E / S en .NET tienen un
...Async()
método correspondiente que puede invocar, que devuelveTask
casi de inmediato. Puede agregar devoluciones de llamada a estoTask
para especificar el código que desea ejecutar cuando se complete la operación asincrónica. También puede especificar en qué subproceso desea que se ejecute ese código, y puede proporcionar un token que la operación asincrónica puede verificar de vez en cuando para ver si decidió cancelar la tarea asincrónica, dándole la oportunidad de detener su trabajo rápidamente Y con gracia.Hasta que
async/await
se agregaron las palabras clave, C # era mucho más obvio sobre cómo se invoca el código de devolución de llamada, porque esas devoluciones de llamada eran en forma de delegados que usted asoció con la tarea. Para poder seguir brindándole el beneficio de utilizar la...Async()
operación, al tiempo que evita la complejidad del código,async/await
abstrae la creación de esos delegados. Pero todavía están allí en el código compilado.Por lo tanto, puede hacer que su controlador de eventos de IU
await
funcione de E / S, liberando el subproceso de la IU para hacer otras cosas y volviendo más o menos automáticamente al subproceso de la IU una vez que haya terminado de leer el archivo, sin tener que hacerlo crear un nuevo hilofuente