Soy nuevo en el C#s await/async y actualmente jugando un poco.

En mi situación tengo un cliente simple objeto que tiene una WebRequest de la propiedad. El cliente debe enviar periódicamente vivo-mensajes a través de la WebRequests RequestStream.
Este es el constructor de la cliente-objeto:

public Client()
{
    _webRequest = WebRequest.Create("some url");
    _webRequest.Method = "POST";

    IsRunning = true;

    //--> how to start the 'async' method (see below)
}

y el asíncrono vivo-remitente método

private async void SendAliveMessageAsync()
{
    const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
    var seconds = 0;
    while (IsRunning)
    {
        if (seconds % 10 == 0)
        {
            await new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage);
        }

        await Task.Delay(1000);
        seconds++;
    }
}

¿Cómo debe el método se inicia?

nuevo Hilo(SendAliveMessageAsync).Start();

o

Tarea.Ejecutar(SendAliveMessageAsync); //cambiar el tipo de regresar a la Tarea

o

esperan SendAliveMessageAsync(); //produce un error como el constructor no es async

Mi pregunta es más acerca de mi comprensión personal de await/async que supongo que puede estar equivocado en algunos puntos.

La tercera opción es tirar

The 'await' operator can only be used in a method or lambda marked with the 'async' modifier
  • Primero tienes que decidir si este es un fuego-y-olvidar el método o algo que usted desea que esperar.
  • De hecho, es un fuego-y-olvidar ya que simplemente debe iniciar un Thread que enviar vivo de los mensajes.
  • Luego await es inútil ya que no estás realmente interesado en esperar a que se complete. Yo simplemente uso el hilo de enfoque. Personalmente considero que las tareas de pequeñas unidades de trabajo que hay que ejecutar de forma asincrónica.
  • Nunca se debe llamar new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage) – esperan o no – como StreamWriter es IDisposable y se debe desechar de una vez utilizado. Esta sintaxis no permiten llamar a .Dipose().
  • Buena y válida punto. Pero este no es el código de trabajo. El StreamWriter también se inicializa en el constructor, pero por razones de simplicidad no se muestra aquí.
InformationsquelleAutor KingKerosin | 2016-01-15

5 Comentarios

  1. 7

    ¿Cómo debe el método se inicia?

    Yo voto por «ninguna de las anteriores». 🙂

    «Disparar y olvidar» es un escenario difícil de manejar correctamente. En particular, el manejo de errores es siempre problemático. En este caso, async void puede sorprender.

    Prefiero guardar explícitamente las tareas si no estoy awaiting inmediatamente:

    private async Task SendAliveMessageAsync();
    
    public Task KeepaliveTask { get; private set; }
    
    public Client()
    {
      ...
      KeepaliveTask = SendAliveMessageAsync();
    }

    Este, al menos, permite a los consumidores de Client detectar y recuperarse de las excepciones lanzadas por el SendAliveMessageAsync método.

    En una nota de lado, este patrón es casi equivalente a la de mi «inicialización asincrónica» patrón.

  2. 4

    Editado como respuesta anterior estaba equivocado:

    Como es en el constructor, creo que tendría que girar un nuevo hilo para ello. Yo, personalmente, hacer que el uso de

    Tarea.De la fábrica.StartNew(() => SendAliveMessageAsync());

    • El código se ejecuta en el constructor. Llamar await no funciona en constructores.
    • Ups, tienes razón.
    • Hay alguna diferencia entre Task.Run y Task.Factory.StartNew? O es sólo su opinión personal?
    • Para ser honesto, yo uso la Tarea.De la fábrica.StartNew de la fuerza de la costumbre; la Tarea.Correr es bastante parecido a un acceso directo, así que si prefieres usar que ir a la derecha por delante.
  3. 0

    Ya que es un fuego y olvidar operación, usted debe comenzar utilizando

    SendAliveMessageAsync();

    Nota que await no se inicia una nueva Task. Es sólo sintáctica de azúcar que esperar un Task para completar.

    Un nuevo subproceso se inicia con Task.Run.

    Así que dentro de SendAliveMessageAsync usted debe comenzar una nueva Tarea:

    private async Task SendAliveMessageAsync()
    {
        const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
        await Task.Run( () => {
            var seconds = 0;
            while (IsRunning)
            {
                if (seconds % 10 == 0)
                {
                    await new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage);
                }
    
                await Task.Delay(1000);
                seconds++;
            }
        });
    }
    • El código se ejecuta en el constructor. Llamar await no funciona en constructores.
    • Pero es llamar con SendAliveMessageAsync(); no de forma sincrónica y por lo tanto el Constructor nunca volverá (debido a los métodos while)?
    • oh, wow, ¿cómo podría echo de menos que. Gracias por el recordatorio
    • Sí, ese bucle en un constructor es malo. Los constructores deben completar antes posible y deben nunca el riesgo de una excepción.
    • véase mi edición
  4. 0

    He aquí una opción ya que no se puede llamar await desde el interior de un constructor.

    Me gustaría sugerir el uso de Microsoft Reactiva del Marco (NuGet «Rx-Principal»).

    El código sería este:

    public class Client
    {
        System.Net.WebRequest _webRequest = null;
        IDisposable _subscription = null;
    
        public Client()
        {
            _webRequest = System.Net.WebRequest.Create("some url");
            _webRequest.Method = "POST";
    
            const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
    
            var keepAlives =
                from n in Observable.Interval(TimeSpan.FromSeconds(10.0))
                from u in Observable.Using(
                    () => new StreamWriter(_webRequest.GetRequestStream()),
                    sw => Observable.FromAsync(() => sw.WriteLineAsync(keepAliveMessage)))
                select u;
    
            _subscription = keepAlives.Subscribe();
        }
    }

    Este código se encarga de toda la rosca necesaria y adecuadamente dispone de la StreamWriter como va.

    Cuando desee detener el mantener abiertas acaba de llamar _subscription.Dispose().

  5. 0

    En su código no es necesario el uso de async/await, acaba de establecer un nuevo hilo para realizar la operación.

    private void SendAliveMessage()
    {
        const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
        var sreamWriter = new StreamWriter(_webRequest.GetRequestStream());
        while (IsRunning)
        {
            sreamWriter.WriteLine(keepAliveMessage);
            Thread.Sleep(10 * 1000);
        }    
    }

    Utilizando Task.Factory.StartNew(SendAliveMessage, TaskCreationOptions.LongRunning) para realizar la operación.

    Si usted realmente desea el uso de async/await patrón, sólo tiene que llamar en el constructor sin esperan modificador y la olvide.

    public Client()
    {
        _webRequest = WebRequest.Create("some url");
        _webRequest.Method = "POST";
    
        IsRunning = true;
    
        SendAliveMessageAsync();    //just call it and forget it.
    }

    Creo que no es buena idea establecer una larga subproceso en ejecución o el uso de async/await patrón. Temporizadores tal vez más adecuado en esta situación.

Dejar respuesta

Please enter your comment!
Please enter your name here