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

问题描述 投票:0回答:7

有时我需要启动一个工作非常慢的异步作业。我不在乎那份工作是否成功,我需要继续处理我当前的话题。

有时我需要发送电子邮件或短信,但速度非常慢。我需要尽快回复网络客户端所以我不想

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.

如果我真的

await
了怎么办? C# 中“即发即忘”的最佳实践是什么,只需调用异步方法而不等待其完成?

c# asynchronous async-await
7个回答
37
投票

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

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

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

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


34
投票

如果你真的只想解雇然后忘记。简单地不要调用使用等待。

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

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

如果你想稍后使用结果输出,它会变得更棘手。但如果真的着火了,忘了上面的方法应该行得通

取消令牌允许中断和取消程序。如果您正在使用取消令牌,您将需要在从直接检索到调用方法(一直到 Turtles 一直向下)的任何地方使用它。

我用

ConfigureAwait(false)
来防止死锁。 在这里了解更多信息


编辑

查看使用“Task.Factory.StartNew”的第二个答案我前段时间给出了这个答案。当时我没有意识到我当时的做法并不能确保完成。


8
投票

如果你需要在你的函数中使用

async
你也可以使用一个 discard 变量 而不要使用 await。如果您有多个异步函数调用但您不需要等待所有调用,这也很有用。

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

7
投票

正如 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(() =>  _emailService.SendEmailAsync());
确实会在新线程上工作。


0
投票

这完全取决于您的 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.
    }

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


-1
投票

我很好奇为什么没有人建议这样做。

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

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


-1
投票

Task.Run 与 async await 结合使用对我来说效果最好,即使没有丢弃。我认为它在自己的上下文中运行,并且在父上下文终止时不会终止,例如在具有依赖注入的 Azure 函数中。

Task.Run(async () => await _emailService.SendEmailAsync());
© www.soinside.com 2019 - 2024. All rights reserved.