使用异步而不等待?

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

考虑使用异步而不等待

认为您可能误解了异步的作用。警告是 完全正确:如果您将方法标记为异步但不使用等待 任何地方,那么你的方法都不会是异步的。如果你调用它,所有 方法内的代码将同步执行。

我想编写一个应该异步运行但不需要使用await 的方法。例如,当使用线程时

public async Task PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
}

我想调用

PushCallAsync
并运行异步,并且不想使用等待。

我可以在 C# 中使用 async 而不使用 wait 吗?

c# task-parallel-library async-await c#-5.0
5个回答
42
投票

你还是误会了

async
async
关键字确实not意味着“在另一个线程上运行”。

要将某些代码推送到另一个线程上,您需要显式地执行此操作,例如,

Task.Run
:

await Task.Run(() => Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId));

我有一篇

async
/
await
介绍文章
,您可能会觉得有帮助。


37
投票

如果您的

Logger.LogInfo
已经是异步的,这就足够了:

public void PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
}

如果不是,则异步启动而不等待

public void PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    Task.Run(() => Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId));
}

6
投票

你误会了

async
。 它实际上只是告诉编译器传播它在后台为您执行的控制流反转。这样整个方法堆栈就被标记为异步。

您实际想要做什么取决于您的问题。 (让我们考虑您的调用

Logger.LogInfo(..)
是一个
async
方法,因为它最终会调用 File.WriteAsync() 左右。

  • 如果您调用的函数是一个 void 事件处理程序,那就没问题。异步调用将在某种程度上(即 File.WriteAsync)在后台发生。您不希望控制流产生任何结果。那就是一劳永逸。
  • 但是,如果您想对结果做任何事情,或者如果您只想继续,那么当
    Logger.LogInfo(..)
    完成时,您必须采取预防措施。当您的方法以某种方式“位于调用堆栈的中间”时,就会出现这种情况。然后 Logger.LogInfo(..) 通常会返回
    Task
    并且您可以等待。但要小心调用task.Wait(),因为它会死锁你的GUI线程。而是使用等待或返回任务(然后你可以省略异步):
    
    
    

public void PushCallAsync(CallNotificationInfo callNotificationInfo) { return Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId); }



public async void PushCallAsync(CallNotificationInfo callNotificationInfo) { await Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId); }



3
投票

public async Task PushCallAsync(CallNotificationInfo callNotificationInfo) { Logger.LogInfo("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId, }

    Logger.LogInfo
  • 同步调用
  • async
  • 关键字为方法
    PushCallAsync
    提供了
    await
    能力,但它从不等待任何东西
  • 如果您打算使该方法异步运行(正如名称
PushCallAsync

所暗示的那样),您必须找到同步调用

LogInfo
的替代方法。
如果存在 

LogInfoAsync

方法,尝试使用

await
来规避是不明智的。等待很重要,因为:

它捕获并抛出任务执行时可能发生的异常 - 否则这些异常将丢失/未处理
  • 通过等待结果来保证执行顺序
  • 如果您特别想要“即发即忘”行为
,即不依赖于执行顺序(例如,在这种情况下,您不关心日志消息的顺序),则可以调用

LogInfoAsync(),而无需等待结果。

由于您没有使用任何 
await

,因此您不会标记该方法

async

。使它异步的不是 
async
 关键字,而是它异步调用其他方法。
public Task PushCallAsync(CallNotificationInfo callNotificationInfo)
{
    // Fire and forget - we do not care about the result, failure, or order of this log message
    _ = Logger.LogInfoAsync("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
    Task.CompletedTask;
}

或非异步
public void PushCall(CallNotificationInfo callNotificationInfo)
{
    // Fire and forget - we do not care about the result, failure, or order of this log message
    _ = Logger.LogInfoAsync("Pushing new call {0} with {1} id".Fill(callNotificationInfo.CallerId,
}

请注意,方法名称
Push
暗示它是有序的。因此,如果您不关心顺序,我会以不同的方式命名它。否则,正如 Push 所暗示的那样,使用

await 来确保顺序是正确的。


如果Logger.LogInfo是同步方法,那么整个调用无论如何都是同步的。如果您只想在单独的线程中执行代码,则异步不是完成该工作的工具。尝试使用线程池代替:

2
投票
ThreadPool.QueueUserWorkItem( foo => PushCallAsync(callNotificationInfo) );


© www.soinside.com 2019 - 2024. All rights reserved.