_ = Task.Run 与 async void | Task.Run 与 Async Sub

Fox*_*Fox 6 .net c# vb.net asynchronous async-await

在控制台应用程序中。我需要从主线程加载一些长时间运行的代码(网络内容、REST 调用)。我想将它传递给后台线程并且不阻塞调用线程。我将调用该方法中的事件来处理其结果。

这样做有什么区别吗?

public async Task MainThreadAsync() {
    _ = Task.Run(async () => await DoSomethingAsync());
    // Continue with other stuff and don't care about DoSomethingAsync()
}

private async Task DoSomethingAsync() {
    // Doing long running stuff
}
Run Code Online (Sandbox Code Playgroud)

或这样做?

public async Task MainThreadAsync() {
    DoSomethingAsync();
    // Continue with other stuff and don't care about DoSomethingAsync()
}

private async void DoSomethingAsync() {
    // Doing long running stuff
}
Run Code Online (Sandbox Code Playgroud)

VB.Net:

Public Async Function MainThreadAsync() As Task 
    Task.Run(Async Function() As Task
                 Await DoSomethingAsync()
             End Function)
    ' Continue with other stuff and don't care about DoSomethingAsync()
End Function

Private Async Function DoSomethingAsync() As Task
    ' Doing long running stuff
End Function
Run Code Online (Sandbox Code Playgroud)

Public Async Function MainThreadAsync() As Task 
    DoSomethingAsync()
    ' Continue with other stuff and don't care about DoSomethingAsync()
End Function

Private Async Sub DoSomethingAsync()
    ' Doing long running stuff
End Sub
Run Code Online (Sandbox Code Playgroud)

或者还有更好的方法吗?另外,c#和vb.net在这方面有区别吗?

Mar*_*ell 6

首先:不要使用 async void. 我意识到它表达了你想要的语义,但是有一些框架内部结构如果遇到它就会主动爆炸(这是一个漫长而无趣的故事),所以:不要进行这种实践。

让我们假设我们有:

private async Task DoSomething() {...}
Run Code Online (Sandbox Code Playgroud)

出于这个原因,在这两种情况下。


这里的主要区别是,从调用者的角度来看,不能保证不会DoSomething同步运行。所以在这种情况下:

public async task MainThread() {
    _ = DoSomething(); // note use of discard here, because we're not awaiting it
}
Run Code Online (Sandbox Code Playgroud)

DoSomething将在主线程上运行至少到第一个await- 具体来说,第一个incomplete await。好消息是:您只需添加:

await Task.Yield();
Run Code Online (Sandbox Code Playgroud)

作为第一行DoSomething(),它保证立即返回给调用者(因为Task.Yield本质上总是不完整的),避免必须通过Task.Run. 在内部,Task.Yield()执行与非常相似的Task.Run()操作,但它可以跳过一些不必要的部分。

把所有这些放在一起 - 如果是我,我会:

public async Task MainThread() {
    _ = DoSomething();

    // Continue with other stuff and don't care about DoSomething()
}
private async Task DoSomething() {
    await Task.Yield();

    // Doing long running stuff
}
Run Code Online (Sandbox Code Playgroud)