猿问

“等待任务;Run();返回”和“返回Task.Run()”之间有什么区别?

“等待任务;Run();返回”和“返回Task.Run()”之间有什么区别?

以下两段代码在概念上是否有区别:

async Task TestAsync() 
{
    await Task.Run(() => DoSomeWork());
}

Task TestAsync() 
{
    return Task.Run(() => DoSomeWork());
}

生成的代码也不同吗?

编辑:避免混淆Task.Run,类似的情况:

async Task TestAsync() 
{
    await Task.Delay(1000);
}

Task TestAsync() 
{
    return Task.Delay(1000);
}

延迟更新:除了公认的答案,LocalCallContext获取处理:即使在没有异步的情况下,也会恢复CallContext.LogicalGetData。为什么?


饮歌长啸
浏览 3801回答 3
3回答

holdtom

,除了下面解释的异常传播行为的差异之外,还有另一个微妙的区别:async/await在非默认的同步上下文中,版本更容易出现死锁。例如,以下内容将死锁在WinForms或WPF应用程序中:static&nbsp;async&nbsp;Task&nbsp;TestAsync(){ &nbsp;&nbsp;&nbsp;&nbsp;await&nbsp;Task.Delay(1000);}void&nbsp;Form_Load(object&nbsp;sender,&nbsp;EventArgs&nbsp;e){ &nbsp;&nbsp;&nbsp;&nbsp;TestAsync().Wait();&nbsp;//&nbsp;dead-lock&nbsp;here}将其更改为非异步版本,它不会死锁:Task&nbsp;TestAsync()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Task.Delay(1000);}StephenCleary在他的书中很好地解释了死锁的本质。博客.另一个主要的区别是异常传播中抛出的异常。async Task方法时,获取将存储在返回的Task对象并保持休眠状态,直到通过await task,&nbsp;task.Wait(),&nbsp;task.Result或task.GetAwaiter().GetResult()..即使从同步部分async方法。考虑以下代码,其中OneTestAsync和AnotherTestAsync行为完全不同:static&nbsp;async&nbsp;Task&nbsp;OneTestAsync(int&nbsp;n){ &nbsp;&nbsp;&nbsp;&nbsp;await&nbsp;Task.Delay(n);}static&nbsp;Task&nbsp;AnotherTestAsync(int&nbsp;n){ &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Task.Delay(n);} &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;call&nbsp;DoTestAsync&nbsp;with&nbsp;either&nbsp;OneTestAsync&nbsp;or&nbsp;AnotherTestAsync&nbsp;as&nbsp;whatTeststatic&nbsp;void&nbsp;DoTestAsync(Func<int,&nbsp;Task>&nbsp;whatTest,&nbsp;int&nbsp;n){ &nbsp;&nbsp;&nbsp;&nbsp;Task&nbsp;task&nbsp;=&nbsp;null; &nbsp;&nbsp;&nbsp;&nbsp;try &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;start&nbsp;the&nbsp;task &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;task&nbsp;=&nbsp;whatTest(n); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;do&nbsp;some&nbsp;other&nbsp;stuff,&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;while&nbsp;the&nbsp;task&nbsp;is&nbsp;pending &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.Write("Press&nbsp;enter&nbsp;to&nbsp;continue"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.ReadLine(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;task.Wait(); &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;catch&nbsp;(Exception&nbsp;ex) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.Write("Error:&nbsp;"&nbsp;+&nbsp;ex.Message); &nbsp;&nbsp;&nbsp;&nbsp;}}如果我打电话DoTestAsync(OneTestAsync, -2),它产生以下输出:Press&nbsp;enter&nbsp;to&nbsp;continue Error:&nbsp;One&nbsp;or&nbsp;more&nbsp;errors&nbsp;occurred.await&nbsp;Task.Delay Error:&nbsp;2nd注意,我不得不按下进入去看它。现在,如果我打电话DoTestAsync(AnotherTestAsync, -2),内部的代码工作流。DoTestAsync完全不同,输出也不一样。这一次,我没有被要求按进入:Error:&nbsp;The&nbsp;value&nbsp;needs&nbsp;to&nbsp;be&nbsp;either&nbsp;-1&nbsp;(signifying&nbsp;an&nbsp;infinite&nbsp;timeout),&nbsp;0&nbsp;or&nbsp;a&nbsp;positive&nbsp;integer. Parameter&nbsp;name:&nbsp;millisecondsDelayError:&nbsp;1st在这两种情况下Task.Delay(-2)在开始时抛出,同时验证其参数。这可能是一个虚构的场景,但在理论上Task.Delay(1000)也可能抛出,例如,当基础系统计时器API失败时。另外,错误传播逻辑对于async void方法(相对于async Task方法)。内部引发的异常。async void方法将立即在当前线程的同步上下文上重新抛出(通过SynchronizationContext.Post),如果当前线程有一个(SynchronizationContext.Current != null)..否则,它将被重新抛出ThreadPool.QueueUserWorkItem)。调用方没有机会在同一堆栈帧上处理此异常。我张贴了一些关于tpl异常处理行为的更多细节。这里和这里.Q*是否可以模拟async非异步的方法。Task-基于方法,以便后者不会抛出相同的堆栈帧?A*如果真的需要,那么是的,有一个诀窍://&nbsp;asyncasync&nbsp;Task<int>&nbsp;MethodAsync(int&nbsp;arg){ &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(arg&nbsp;<&nbsp;0) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;ArgumentException("arg"); &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;... &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;42&nbsp;+&nbsp;arg;}//&nbsp;non-asyncTask<int>&nbsp;MethodAsync(int&nbsp;arg){ &nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;task&nbsp;=&nbsp;new&nbsp;Task<int>(()&nbsp;=>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(arg&nbsp;<&nbsp;0) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;ArgumentException("arg"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;... &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;42&nbsp;+&nbsp;arg; &nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;task.RunSynchronously(TaskScheduler.Default); &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;task;}但请注意,在一定条件下(比如在堆栈太深的时候),RunSynchronously仍然可以异步执行。

跃然一笑

.之间的区别是什么?async&nbsp;Task&nbsp;TestAsync()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;await&nbsp;Task.Delay(1000);}和Task&nbsp;TestAsync()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Task.Delay(1000);}?我对这个问题感到困惑。让我用另一个问题来回答你的问题。两者有什么区别?Func<int>&nbsp;MakeFunction(){ &nbsp;&nbsp;&nbsp;&nbsp;Func<int>&nbsp;f&nbsp;=&nbsp;()=>1; &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;()=>f();}和Func<int>&nbsp;MakeFunction(){ &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;()=>1;}?不管我的两件事有什么区别,你的两件事也有相同的区别。
随时随地看视频慕课网APP
我要回答