为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
饮歌长啸 2019-06-21 13:02:00
“等待任务;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。为什么?
查看完整描述

3 回答

?
holdtom

TA贡献1805条经验 获得超10个赞

,除了下面解释的异常传播行为的差异之外,还有另一个微妙的区别:async/await在非默认的同步上下文中,版本更容易出现死锁。例如,以下内容将死锁在WinForms或WPF应用程序中:

static async Task TestAsync(){
    await Task.Delay(1000);}void Form_Load(object sender, EventArgs e){
    TestAsync().Wait(); // dead-lock here}

将其更改为非异步版本,它不会死锁:

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

StephenCleary在他的书中很好地解释了死锁的本质。博客.



另一个主要的区别是异常传播中抛出的异常。async Task方法时,获取将存储在返回的Task对象并保持休眠状态,直到通过await tasktask.Wait()task.Resulttask.GetAwaiter().GetResult()..即使从同步部分async方法。


考虑以下代码,其中OneTestAsyncAnotherTestAsync行为完全不同:

static async Task OneTestAsync(int n){
    await Task.Delay(n);}static Task AnotherTestAsync(int n){
    return Task.Delay(n);}
    // call DoTestAsync with either OneTestAsync or AnotherTestAsync as whatTeststatic void DoTestAsync(Func<int, Task> whatTest, int n){
    Task task = null;
    try
    {
        // start the task
        task = whatTest(n);

        // do some other stuff, 
        // while the task is pending
        Console.Write("Press enter to continue");
        Console.ReadLine();
        task.Wait();
    }
    catch (Exception ex)
    {
        Console.Write("Error: " + ex.Message);
    }}

如果我打电话DoTestAsync(OneTestAsync, -2),它产生以下输出:

Press enter to continue
Error: One or more errors occurred.await Task.Delay
Error: 2nd

注意,我不得不按下进入去看它。

现在,如果我打电话DoTestAsync(AnotherTestAsync, -2),内部的代码工作流。DoTestAsync完全不同,输出也不一样。这一次,我没有被要求按进入:

Error: The value needs to be either -1 (signifying an infinite timeout), 0 or a positive integer.
Parameter name: millisecondsDelayError: 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*如果真的需要,那么是的,有一个诀窍:

// asyncasync Task<int> MethodAsync(int arg){
    if (arg < 0)
        throw new ArgumentException("arg");
    // ...
    return 42 + arg;}// non-asyncTask<int> MethodAsync(int arg){
    var task = new Task<int>(() => 
    {
        if (arg < 0)
            throw new ArgumentException("arg");
        // ...
        return 42 + arg;
    });

    task.RunSynchronously(TaskScheduler.Default);
    return task;}

但请注意,在一定条件下(比如在堆栈太深的时候),RunSynchronously仍然可以异步执行。


查看完整回答
反对 回复 2019-06-21
?
跃然一笑

TA贡献1826条经验 获得超6个赞

.之间的区别是什么?

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

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

?

我对这个问题感到困惑。让我用另一个问题来回答你的问题。两者有什么区别?

Func<int> MakeFunction(){
    Func<int> f = ()=>1;
    return ()=>f();}

Func<int> MakeFunction(){
    return ()=>1;}

?

不管我的两件事有什么区别,你的两件事也有相同的区别。


查看完整回答
反对 回复 2019-06-21
  • 3 回答
  • 0 关注
  • 3800 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信