【问题标题】:C# How to start an async method without await its complete?C#如何在不等待完成的情况下启动异步方法?
【发布时间】:2019-04-10 12:33:15
【问题描述】:

有时我需要启动一个运行速度很慢的异步作业。我不在乎那份工作是否成功,我需要继续处理我当前的线程。

就像有时我需要发送一封电子邮件或短信,它的工作速度很慢。我需要尽快回复网络客户端,所以我不想await它。

我用谷歌搜索了这个问题,有些文章建议我这样写:

// This method has to be async
public async Task<Response> SomeHTTPAction()
{
    // Some logic...
    // ...

    // Send an Email but don't care if it successfully sent.
    Task.Run(() =>  _emailService.SendEmailAsync());
    return MyRespond();
}

或者像这样:

// This method has to be async
public async Task<Response> SomeHTTPAction()
{
    // Some logic...
    // ...

    // Send an Email but don't care if it successfully sent.
    Task.Factory.StartNew(() =>  _emailService.SendEmailAsync());
    return MyRespond();
}

会有一个警告说:before the call is completed. Consider applying the 'await' operator to the result of the call.

如果我真的awaited 怎么办? C# 中“触发并忘记”的最佳实践是什么,只需调用异步方法而不等待其完成?

【问题讨论】:

  • 只是不要将函数标记为异步,所以函数不应该期望它的结果被等待?
  • 更好的方法是让这些操作在进程之外运行,即发布到像 rabbitmq 这样的队列,让订阅者接收这些消息,然后发送电子邮件/短信
  • 到目前为止你发现了什么信息? “C# async fire and forget”的快速谷歌显示有很多可用的资源..
  • 这真的不是设计的问题吗?在您知道操作是否完成之前返回MyRespond 有什么意义?

标签: c# asynchronous


【解决方案1】:

如果你真的只是想开火然后忘记。根本不要调用使用等待。

// It is a good idea to add CancellationTokens
var asyncProcedure = SomeHTTPAction(cancellationToken).ConfigureAwait(false);

// Or If not simply do:
var asyncProcedure = SomeHTTPAction().ConfigureAwait(false);

如果您想稍后使用结果输出,它会变得更加棘手。但如果它真的很火,忘记上面应该工作

取消标记允许中断和取消过程。如果您使用的是 Cancellation 令牌,您将需要在从检索到调用方法的所有地方使用它(一直到 Turtles)。

我使用ConfigureAwait(false) 来防止死锁。 Here了解更多信息

【讨论】:

  • @MickyD 主要是因为 OP 如何拥有它,但是有了这个,您可以稍后使用 .GetAwaiter().GetResult() 检索该值
  • 我们为什么使用cancellationToken以及如何使用?
【解决方案2】:

独立丢弃是避免此警告的最佳方法。

_ = Task.Run(() =>  _emailService.SendEmailAsync());

丢弃是虚拟变量,可用于忽略异步操作返回的任务对象。

https://docs.microsoft.com/en-us/dotnet/csharp/discards#a-standalone-discard

【讨论】:

  • 这个答案是正确的。我投反对票只是因为它与 MrMaavin 的 earlier answer 非常相似。
【解决方案3】:

正如 Amadan 在评论中所说,您需要从函数中删除异步。那么它将停止给你警告。

// This method has to be async
public Response SomeHTTPAction()
{
     // Some logic...
     // ...
     // Send an Email but don't care if it successfully sent.
     Task.Factory.StartNew(() =>  _emailService.SendEmailAsync());
     return MyRespond();
}

Task.Factory.StartNew(() =&gt; _emailService.SendEmailAsync()); 确实可以在新线程上工作。

【讨论】:

    【解决方案4】:

    如果您需要在函数中使用async,您也可以使用丢弃变量,不要使用等待。如果您有多个异步函数调用但您不需要等待所有它们,这也很有用。

    public async function(){
        var tmp = await asyncfunction();
        ...
        _ = _httpClient.PutAsync(url, content);
        ...
    }
    

    【讨论】:

      【解决方案5】:

      我很好奇为什么不建议这样做。

      new Thread(() =>
      {
          Thread.CurrentThread.IsBackground = true;
          //what ever code here...e.g.
          DoSomething();
          UpdateSomething();
      }).Start();  
      

      它只是触发一个单独的线程。

      【讨论】:

      • 这将创建一个新线程并在该新线程上运行方法,这非常昂贵/消耗资源。不是相关问题的答案。
      【解决方案6】:

      这完全取决于您的 Async 方法接受什么。通常它会接受一个也举办活动的“特殊”课程。您可以将回调方法订阅到该事件并将其与方法一起传递。完成后,将调用您的回调方法。

      一个例子(对于套接字)是:

          public void CreateSocket()
          {
              Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
              SocketAsyncEventArgs sockAsync = new SocketAsyncEventArgs();
              sockAsync.Completed += SockAsync_Completed;
      
              s.ConnectAsync(sockAsync);
          }
      
          private void SockAsync_Completed(object sender, SocketAsyncEventArgs e)
          {
              //Do stuff with your callback object.
          }
      

      这完全取决于您尝试调用的方法可以接受什么。我会专门查看文档以获取更多帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-15
        • 1970-01-01
        • 1970-01-01
        • 2013-08-19
        • 2019-10-19
        相关资源
        最近更新 更多