以线程或任务方式启动异步方法

时间:2016-01-15 09:31:48

标签: c# asynchronous async-await webrequest

我是C# await/async的新手,目前正在玩一下。

在我的场景中,我有一个简单的客户端对象,它具有WebRequest属性。客户端应定期通过WebRequest RequestStream发送活动消息。 这是client-object的构造函数:

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

    IsRunning = true;

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

和async alive-sender方法

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

该方法应该如何开始?

  

new Thread(SendAliveMessageAsync).Start();

  

Task.Run(SendAliveMessageAsync); //将返回类型更改为Task

  

await SendAliveMessageAsync(); //失败,因为构造函数不是异步

我的问题更多是关于我对await/async的个人理解,我认为在某些方面可能是错误的。

第三个选项是抛出

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

5 个答案:

答案 0 :(得分:7)

  

该方法应该如何开始?

我投票赞成以上所有"。 :)

"火与忘记"是一个难以正确处理的方案。特别是,错误处理总是有问题的。在这种情况下,async void可能会让您感到惊讶。

如果我不立即await,我更愿意明确保存这些任务:

private async Task SendAliveMessageAsync();

public Task KeepaliveTask { get; private set; }

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

这至少允许Client的使用者检测并从SendAliveMessageAsync方法抛出的异常中恢复。

另一方面,这种模式几乎等同于我的documentation

答案 1 :(得分:3)

之前的回答是错误的:

正如构造函数中的那样,我认为你必须为它启动一个新的线程。我个人会这样做

  

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

答案 2 :(得分:0)

由于它是 fire and forget 操作,您应该使用

启动它
SendAliveMessageAsync();

请注意,await无法启动新的Task。等待Task完成只是语法糖 使用Task.Run启动新线程。

所以在SendAliveMessageAsync内你应该开始一个新的任务:

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

答案 3 :(得分:0)

这是一个选项,因为您无法从构造函数中调用await

我建议使用Microsoft的Reactive Framework(NuGet" Rx-Main")。

代码如下所示:

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

此代码处理所需的所有线程并正确处理StreamWriter

每当你想要停止保持活动时,只需致电_subscription.Dispose()

答案 4 :(得分:0)

在你的代码中,不需要使用async / await,只需设置一个新线程来执行长操作。

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

使用Task.Factory.StartNew(SendAliveMessage, TaskCreationOptions.LongRunning)执行操作。

如果你真的想使用async / await模式,只需在构造函数中调用它而不使用await修饰符并忘记它。

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

    IsRunning = true;

    SendAliveMessageAsync();    //just call it and forget it.
}

我认为设置一个长时间运行的线程或使用async / await模式并不是一个好主意。定时器可能更适合这种情况。

相关问题