Mejores prácticas en programación asincrónica .Net

La programación asincrónica le permite realizar operaciones de E / S que consumen muchos recursos sin tener que bloquear el hilo principal o en ejecución de la aplicación. Aunque es beneficioso y aparentemente fácil de implementar, conlleva una gran complejidad y riesgos. Los riesgos potenciales asociados con la programación asincrónica, particularmente el uso incorrecto de la programación asincrónica al no seguir las prácticas recomendadas, incluyen interbloqueos, bloqueos de procesos e incluso un rendimiento lento. También debe dominar la escritura y la depuración de código asincrónico.

Evite tener un tipo de retorno vacío en métodos asincrónicos

Un método en C # se convierte en un método asincrónico utilizando la palabra clave async en la firma del método. Puede tener una o más palabras clave en espera dentro de un método asincrónico. La palabra clave await se utiliza para indicar el punto de suspensión. Un método asincrónico en C # puede tener cualquiera de estos tipos de retorno: Task, Task y void. La palabra clave "await" se utiliza en un método asincrónico para informar al compilador que el método puede tener un punto de suspensión y reanudación.

Tenga en cuenta que cuando se usa el TPL, el equivalente a devolver vacío en TPL es una tarea asíncrona. Debe tener en cuenta que async void es y solo debe usarse para eventos async. Si lo usa en cualquier otro lugar, se encontrará con errores. En otras palabras, no se recomienda un método asincrónico que tenga void como tipo de retorno. porque los métodos asíncronos que devuelven void tienen una semántica diferente cuando trabaja con excepciones en su aplicación.

Cuando ocurre una excepción en un método asíncrono que tiene un tipo de retorno de Tarea o Tarea, el objeto de excepción se almacena dentro del objeto Tarea. Por el contrario, si tiene un método asincrónico con un tipo de retorno de vacío, no hay ningún objeto Task asociado. Estas excepciones se generan en el SynchronizationContext que estaba activo en el momento en que se llamó al método asincrónico. En otras palabras, no puede manejar excepciones generadas dentro de un método void asincrónico utilizando controladores de excepciones escritos dentro del método asincrónico. Los métodos asincrónicos que tienen un tipo de retorno de vacío también son difíciles de probar debido a esta diferencia en la semántica de manejo de errores. Para su información, la clase SynchronizationContext en el espacio de nombres System.Threading representa un contexto de sincronización en .Net y le ayuda a poner en cola una tarea en otro contexto.

La siguiente lista de códigos ilustra esto. Tiene dos métodos, a saber, Test y TestAsync y el último genera una excepción.

public class AsyncDemo

   {

       public void Test()

       {

           try

           {

               TestAsync();

           }

           catch (Exception ex)

           {

               Console.WriteLine(ex.Message);

           }

       }

       private async void TestAsync()

       {

           throw new Exception("This is an error message");

       }

   }

Así es como puede crear una instancia de la clase AsyncDemo e invocar el método Test.

static void Main(string[] args)

       {

           AsyncDemo obj = new AsyncDemo();

           obj.Test();

           Console.Read();

       }

El método de prueba realiza una llamada al método TestAsync y la llamada se encapsula dentro de un bloque try-catch con la intención de manejar la excepción lanzada dentro del método TestAsync. Sin embargo, la excepción lanzada dentro del método TestAsync nunca será detectada, es decir, manejada dentro del método de llamada Test.

Evite mezclar código asíncrono y síncrono

Nunca debe tener una combinación de código sincrónico y asincrónico. Es una mala práctica de programación bloquear el código asíncrono haciendo llamadas a Task.Wait o Task.Result. Recomendaría usar código asincrónico de un extremo a otro, es la forma más segura de evitar que se introduzcan errores.

Puede evitar los interbloqueos utilizando. ConfigureAwait(continueOnCapturedContext: false)siempre que haga una llamada para esperar. Si no usa esto, el método async se bloqueará en el punto donde se ha llamado a await. En este caso, solo está informando al que espera que no capture el contexto actual. Yo diría que es una buena práctica usar .ConfigureAwait (falso) a menos que tenga una razón específica para no usarlo.

Discutiría más sobre la programación asincrónica en mis futuras publicaciones de blog aquí. Para obtener más información sobre las mejores prácticas en programación asincrónica, puede consultar el excelente artículo de Stephen Cleary en MSDN.