Starting async method as Thread or as Task

后端 未结 5 2004
Happy的楠姐
Happy的楠姐 2021-02-20 17:23

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

相关标签:
5条回答
  • 2021-02-20 17:53

    Here's an option since you can't call await from inside a constructor.

    I would suggest using Microsoft's Reactive Framework (NuGet "Rx-Main").

    The code would look like this:

    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();
        }
    }
    

    This code handles all of the threading required and properly disposes of the StreamWriter as it goes.

    Whenever you want to stop the keep alives just call _subscription.Dispose().

    0 讨论(0)
  • 2021-02-20 18:03

    In your code there is no need to using async/await, just set up a new thread to perform long operation.

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

    Using Task.Factory.StartNew(SendAliveMessage, TaskCreationOptions.LongRunning) to perform the operation.

    If you really want to using async/await pattern, just call it in constructor without await modifier and the forget it.

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

    I think it's not good idea to set up a long running thread or using async/await pattern. Timers maybe more suitable in this situation.

    0 讨论(0)
  • 2021-02-20 18:08

    Edited as previous answer was wrong:

    As it's in the constructor, I think you would have to spin up a new thread for it. I would personally do that using

    Task.Factory.StartNew(() => SendAliveMessageAsync());

    0 讨论(0)
  • 2021-02-20 18:12

    Since it is a fire and forget operation, you should start it using

    SendAliveMessageAsync();
    

    Note that await does not start a new Task. It is just syntactical sugar to wait for a Task to complete.
    A new thread is started using Task.Run.

    So inside SendAliveMessageAsync you should start a new Task:

    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++;
            }
        });
    }
    
    0 讨论(0)
  • 2021-02-20 18:13

    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.

    0 讨论(0)
提交回复
热议问题