C# _=Task.Run vs async void | Task.Run vs async Sub

C# _=Task.Run vs async void | Task.Run vs async Sub,c#,.net,vb.net,asynchronous,async-await,C#,.net,Vb.net,Asynchronous,Async Await,在控制台应用程序中。我需要从主线程加载一些长时间运行的代码(网络内容、REST调用)。我想将它传递到后台线程,并且不阻止调用线程。我将调用该方法中的事件来处理其结果 这样做有什么区别吗 private异步任务DoSomethingAsync(){ //做长跑运动 } 公共异步任务mainthreadsync(){ _=Task.Run(异步()=>wait DoSomethingAsnyc()); //继续其他内容,不要关心DoSomethingAsync() } 那么这样做呢 private

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

这样做有什么区别吗

private异步任务DoSomethingAsync(){
//做长跑运动
}
公共异步任务mainthreadsync(){
_=Task.Run(异步()=>wait DoSomethingAsnyc());
//继续其他内容,不要关心DoSomethingAsync()
}
那么这样做呢

private异步void DoSomethingAsync(){
//做长跑运动
}
公共异步任务mainthreadsync(){
DoSomethingAsync();
//继续其他内容,不要关心DoSomethingAsync()
}

VB.Net:

专用异步函数DoSomethingAsync()作为任务
“做长时间跑步的事情
端函数
公共异步函数mainthreadsync()作为任务
Task.Run(异步函数()作为任务
等待DoSomethingAsync()
终端功能)
'继续其他内容,不要关心DoSomethingAsync()
端函数
vs

Private异步子DoSomethingAsync()
“做长时间跑步的事情
端接头
公共异步函数mainthreadsync()作为任务
DoSomethingAsync()
'继续其他内容,不要关心DoSomethingAsync()
}
还是有更好的办法? 另外,c#和vb.net在这方面有区别吗?

关于c#,这种情况下:

private async void DoSomething() {
    // Doing long running stuff
}
public async task MainThread() {
    DoSomething();
    // Continue with other stuff and don't care about DoSomething()
}
将同步运行
,因为可以等待的是任务,而没有创建任何任务。

但是,该代码:

private async task DoSomething() {
    // Doing long running stuff
}
public async task MainThread() {
    _ = Task.Run(async () => await DoSomething());
    // Continue with other stuff and don't care about DoSomething()
}
将在显式创建和启动新任务时异步运行(fire FiTarget)

首先:不要使用
async void
。我意识到它表达了您想要的语义,但是如果遇到它,有些框架内部会自动爆炸(这是一个冗长、无趣的故事),因此:不要进行这种实践

让我们假设我们有:

private async Task DoSomething(){…}
出于这个原因,在这两种情况下


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

public异步任务MainThread(){
_=DoSomething();//注意这里使用discard,因为我们不等待它
}
DoSomething
将在主线程上至少运行到第一个
wait
——具体来说,第一个未完成的
wait
。好消息是:您可以添加:

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

把这些放在一起——如果是我,我会:

public异步任务MainThread(){
_=DoSomething();
//继续做其他的事情,不要在乎什么
}
私有异步任务DoSomething(){
等待任务;
//做长跑运动
}

是的,有区别。但是,首先让我们跟随并将
Async
后缀附加到异步方法
DoSomething
main线程

private async Task DoSomethingAsync() {
    // Doing long running stuff
}
public async Task MainThreadAsync() {
    _ = Task.Run(async () => await DoSomethingAsync());
    // Continue with other stuff and don't care about DoSomethingAsync()
}
这确保了
DoSomethingAsync
将从头到尾在一个线程中运行。主线程甚至不会运行一行
DoSomethingAsync
方法。它只需安排一个
任务
线程池
中运行,这是一个以纳秒为单位的微小作业,然后继续执行其他任务。
DoSomethingAsync
方法中可能存在的异常将永远不会被观察到。除非您处理该事件(以获取非确定性延迟通知),或者更改App.config中的特定设置,或者您正在.NET Framework 4.0中运行

private async void DoSomething() {
    // Doing long running stuff
}
public async Task MainThreadAsync() {
    DoSomething();
    // Continue with other stuff and don't care about DoSomething()
}
这个函数将在主线程中开始运行
DoSomething
,并在遇到未完成的等待的
await
时返回。从概念上讲,你可以说DoSomething由两部分组成,同步部分和异步部分。第一部分将在主线程中运行,第二部分将在
线程池
线程中运行(因为您的应用程序是一个控制台应用程序,除非您这样做,否则不会运行)


DoSomething
是,因此
DoSomething
方法中可能存在的异常将在当前的
SynchronizationContext
中抛出,如果没有安装,则在
ThreadPool
中抛出。这意味着在引发
AppDomain.UnhandledException
事件后,您的控制台应用程序将无法控制地崩溃。在某些情况下,这可能正是你想要的。例如,如果您100%确信
DoSomething
在正常情况下不应抛出,那么立即崩溃可能比让应用程序在潜在损坏的内部状态下继续运行更可取。一般来说,尽管您应该尽量减少fire and forget tasks和async void方法的使用,因为它们会使您的程序更加混乱和不可预测。

但是当我测试第一个案例时,DoSomething()下面的代码会像第二个版本一样继续执行,不会等待。这是为什么?DoSomething是否同步运行实际上取决于DoSomething的内部-它将同步运行,直到@marcGravel提到的第一个不完整等待(/cc@Fox),如果隐藏在
//做长时间运行的事情
后面的代码是同步的(没有启动任务),那么它就是同步的