Starting async method as Thread or as Task

KingKerosin picture KingKerosin · Jan 15, 2016 · Viewed 31.7k times · Source

I'm new to C#s await/async and currently playing around a bit.

In my scenario I have a simple client-object which has a WebRequest property. The client should send periodically alive-messages over the WebRequests RequestStream. This is the constructor of the client-object:

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

    IsRunning = true;

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

and the async alive-sender method

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++;
    }
}

How should the method be started?

new Thread(SendAliveMessageAsync).Start();

or

Task.Run(SendAliveMessageAsync); // changing the returning type to Task

or

await SendAliveMessageAsync(); // fails as of the constructor is not async

My question is more about my personal understanding of await/async which I guess may be wrong in some points.

The third option is throwing

The 'await' operator can only be used in a method or lambda marked with the 'async' modifier

Answer

Stephen Cleary picture Stephen Cleary · Jan 23, 2016

How should the method be started?

I vote for "none of the above". :)

"Fire and forget" is a difficult scenario to handle correctly. In particular, error handling is always problematic. In this case, async void may surprise you.

I prefer to explicitly save the tasks if I'm not awaiting them immediately:

private async Task SendAliveMessageAsync();

public Task KeepaliveTask { get; private set; }

public Client()
{
  ...
  KeepaliveTask = SendAliveMessageAsync();
}

This at least allows the consumers of Client to detect and recover from exceptions thrown by the SendAliveMessageAsync method.

On a side note, this pattern is almost equivalent to my "asynchronous initialization" pattern.