C#如何在不等待完成的情况下启动异步方法?

Posted

技术标签:

【中文标题】C#如何在不等待完成的情况下启动异步方法?【英文标题】:C# How to start an async method without await its complete? 【发布时间】: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# 中“触发并忘记”的最佳实践是什么,只需调用异步方法而不等待其完成?

【问题讨论】:

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

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

【讨论】:

以上是关于C#如何在不等待完成的情况下启动异步方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不等待完成的情况下调用存储过程?

如何在不使用跟踪句柄的情况下等待未知数量的 Rust 线​​程完成?

C#异步编程概念和使用

如何在不等待 C++ 响应的情况下启动线程?

按值返回值而不等待所有值返回异步 C#

如何在不关闭 Executor 的情况下等待 ThreadPoolExecutor 中的所有任务完成?