之间&QUOT任何差异;等待Task.Run();返回;"和"返回Task.Run()"? [英] Any difference between "await Task.Run(); return;" and "return Task.Run()"?

查看:243
本文介绍了之间&QUOT任何差异;等待Task.Run();返回;"和"返回Task.Run()"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有code以下两片之间的任何概念上的差异:

Is there any conceptual difference between the following two pieces of code:

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

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

是否产生code不同,要么?

Does the generated code differ, either?

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

To avoid confusion with Task.Run, a similar case:

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

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

LATE更新:除了公认的答案,也有在 LocalCallContext 如何被处理的区别:的 CallContext.LogicalGetData得到即使没有异步恢复。为什么呢?

LATE UPDATE: In addition to the accepted answer, there is also a difference in how LocalCallContext gets handled: CallContext.LogicalGetData gets restored even where there is no asynchrony. Why?

推荐答案

更新后,除了在异常传播行为的差异说明如下,还有另一种很微妙的区别:异步 / 等待版本更容易出现在非默认的同步上下文死锁。例如,下面将死锁一个WinForms或WPF应用程序:

Updated, besides the differences in the exception propagation behavior explained below, there's another somewhat subtle difference: the async/await version is more prone to dead-locking on a non-default synchronization context. E.g., the following will dead-lock in a WinForms or WPF application:

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

void Form_Load(object sender, EventArgs e)
{
    TestAsync().Wait(); // dead-lock here
}

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

Change it to the non-async version and it won't dead-lock:

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

死锁的性质由斯蒂芬·克利里在他的博客



另一个主要区别是在异常传播。一个例外,一个异步任务方法中抛出,获取存储在返回的任务直到任务得到通过观察等待任务对象,并保持休眠状态 task.Wait() task.Result task.GetAwaiter()调用getResult()。它被传播,即使从抛出这种方式的同步的的的一部分异步方法。


Another major difference is in exception propagation. An exception, thrown inside an async Task method, gets stored in the returned Task object and remains dormant until the task gets observed via await task, task.Wait(), task.Result or task.GetAwaiter().GetResult(). It is propagated this way even if thrown from the synchronous part of the async method.

考虑以下code,其中 OneTestAsync AnotherTestAsync 表现完全不同:

Consider the following code, where OneTestAsync and AnotherTestAsync behave quite differently:

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 whatTest
static 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),它产生以下输出:

If I call DoTestAsync(OneTestAsync, -2), it produces the following output:


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

请注意,我不得不preSS <大骨节病>输入看看吧。

Note, I had to press Enter to see it.

现在,如果我叫 DoTestAsync(AnotherTestAsync,-2),里面的 DoTestAsync的code的工作流程是完全不同的,所以是输出。这个时候,我并没有被要求preSS <大骨节病>输入

Now, if I call DoTestAsync(AnotherTestAsync, -2), the code workflow inside DoTestAsync is quite different, and so is the output. This time, I wasn't asked to press Enter:


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失败。

In both cases Task.Delay(-2) throws at the beginning, while validating its parameters. This might be a made-up scenario, but in theory Task.Delay(1000) may throw too, e.g., when the underlying system timer API fails.

在一个侧面说明,误差传播逻辑是没有不同的 异步无效方法(而不是异步任务方法)。内部的提出异步无效方法将立即对当前线程的同步上下文重新抛出(通过 SynchronizationContext.Post ),如果当前线程有一个( SynchronizationContext.Current!= NULL)。否则,将可通过 ThreadPool.QueueUserWorkItem 重新抛出)。主叫方没有机会处理同一堆栈帧此异常。

On a side note, the error propagation logic is yet different for async void methods (as opposed to async Task methods). An exception raised inside an async void method will be immediately re-thrown on the the current thread's synchronization context (via SynchronizationContext.Post), if the current thread has one (SynchronizationContext.Current != null). Otherwise, it will be re-thrown via ThreadPool.QueueUserWorkItem). The caller doesn't have a chance to handle this exception on the same stack frame.

我张贴了关于太平人寿的异常处理行为 href=\"http://stackoverflow.com/a/22395161/1768303\">和的这里

I posted some more details about TPL exception handling behaviour here and here.

问::是否有可能模仿的异常传播行为异步非异步方法任务为基础的方法,使后者不会抛出相同的堆栈帧?

Q: Is it possible to mimic the exception propagation behavior of async methods for non-async Task-based methods, so that the latter doesn't throw on the same stack frame?

A :如果真的需要,那么,有一个小窍门为:

A: If really needed, then yes, there is a trick for that:

// async
async Task<int> MethodAsync(int arg)
{
    if (arg < 0)
        throw new ArgumentException("arg");
    // ...
    return 42 + arg;
}

// non-async
Task<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 仍然可以执行异步。

Note however, under certain conditions (like when it's too deep on the stack), RunSynchronously could still execute asynchronously.

这篇关于之间&QUOT任何差异;等待Task.Run();返回;&QUOT;和&QUOT;返回Task.Run()&QUOT;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆