不必要的异步/等待时,等待是最后一次? [英] Unnecessary async/await when await is last?

查看:251
本文介绍了不必要的异步/等待时,等待是最后一次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在处理了不少与异步计谋的最近(读每一个可能的文章,包括斯蒂芬和乔恩的最后2章),但我是来的结论,我不知道这是否是100%正确。 - 因此,我的问题

I've been dealing quite a lot with async await lately (read every possible article including Stephen's and Jon's last 2 chapters) , but I have come to conclusion and I don't know if it's 100% correct. - hence my question .

由于异步只允许字等待为present,我会离开异步一边。

Since async only allows the word await to be present , i'll leave async aside.

AFAIU,等待是所有关于延续。 ,而不是写功能(continuational)code,写同步code。 (我喜欢把它的 callback'able 的code)

AFAIU , await is all about continuation . instead of writing functional (continuational) code , write synchronous code. ( i like to refer it as callback'able code)

所以,当编译器达到等待 - 它将该code至2节并注册要执行的第二部分,第一部分完成后(回调不使用 - 这正是做的)。 (同时工作 - 线程又回来做其他事情)。

So when the compiler reaches await - it splits the code to 2 sections and registers the second part to be executed after the first part is done ( I don't know why the word callback isn't used - which is exactly what is done). ( meanwhile working - the thread is back doing other things).

但看这个code:

public async  Task ProcessAsync()
        {
           Task<string> workTask = SimulateWork();
           string st= await workTask;
           //do something with st
        }

 public    Task <string> SimulateWork()
        {
            return ...
        }

当线程达到等待workTask; 它分裂法2个部分。打完 SimulateWork 已完成 - 该方法的延续:又名: - 执行 //做一些与ST

When thread reaches await workTask; it split the method to 2 sections . so after SimulateWork is finished - the continuation of the method : AKA : //do something with st - is executed.

一切ok

但是,如果该方法是:

public async  Task ProcessAsync()
        {
           Task<string> workTask = SimulateWork();
           await workTask; //i don't care about the result , and I don't have any further commands 
        }

在这里 - 我的的需要延续,这意味着 - 我不需要等待来分割这意味着该方法 - 这不是我'T需要异步/计谋在这里了!,然后我依然会有相同的结果/行为

Here - I don't need continuation , meaning - I don't need the await to split the method which means - I don't need async /await here at all ! and still I will have the same results/behaviour !

所以,我能做到这一点,如:

So I could do it like :

   public void ProcessAsync()
            {
               SimulateWork();
            }

问:

  • 在当时我是100%正确的与我的诊断?

推荐答案

那么,你认为等待下面是多余的,因为这个问题的标题所示:

So, you think the await below is redundant, as the question's title implies:

public async Task ProcessAsync()
{
    Task<string> workTask = SimulateWork();
    await workTask; //i don't care about the result , and I don't have any further 
}

首先,我假设下的等待是最后一个的你的意思的计谋是唯一计谋 。它要尽,否则下面根本就不能编译:

First of all, I assume that under "when await is last" you mean "when the await is the only await". It's got to be that, because otherwise the following simply would not compile:

public async Task ProcessAsync()
{
    await Task.Delay(1000);
    Task<string> workTask = SimulateWork();
    return workTask; 
}

现在,如果它是的只有等待,你的确可以优化它是这样的:

Now, if it's the only await, you can indeed optimize it like this:

public Task ProcessAsync()
{
    Task<string> workTask = SimulateWork();
    return workTask; 
}

不过,它会给你完全不同的异常传播行为,这可能会有一些意想不到的副作用。问题是,现在的异常可能抛出在调用者的堆栈,这取决于如何 SimulateWork 在内部实现的。我张贴这种行为的详细的解释。这通常不会发生与异步 工作 / 任务&LT;&GT; 方法,在这里异常存储在返回工作对象中。它仍然可能发生一个异步无效的方法,但是这是一个不同的故事

However, it would give you completely different exception propagation behavior, which may have some unexpected side effects. The thing is, now exceptions may be thrown on the caller's stack, depending on how SimulateWork is internally implemented. I posted a detailed explanation of this behavior. This normally never happens with async Task/Task<> methods, where exception is stored inside the returned Task object. It still may happen for an async void method, but that's a different story.

因此​​,如果您来电code是准备在异常传播这种差异,它可能是一个好主意,跳过异步/计谋的地方,你可以简单地返回工作代替。

So, if your caller code is ready for such differences in exception propagation, it may be a good idea to skip async/await wherever you can and simply return a Task instead.

另一个问题是,如果你想发出的发射后忘记的通话。通常情况下,你仍然想以某种方式跟踪发射任务的状态,至少在处理任务异常的原因。我无法想象一个情况下,我真的不关心,如果任务无法完成,即使它是所有记录。

Another matter is if you want to issue a fire-and-forget call. Usually, you still want to track the status of the fired task somehow, at least for the reason of handling task exceptions. I could not imagine a case where I would really not care if the task never completes, even if all it does is logging.

所以,对于发射后忘了我通常使用一个辅助异步无效方法存储挂起的任务某处后观察,如:

So, for fire-and-forget I usually use a helper async void method which stores the pending task somewhere for later observation, e.g.:

readonly object _syncLock = new Object();
readonly HashSet<Task> _pendingTasks = new HashSet<Task>();

async void QueueTaskAsync(Task task)
{
    // keep failed/cancelled tasks in the list
    // they will be observed outside
    lock (_syncLock)
        _pendingTasks.Add(task);

    try
    {
        await task;
    }
    catch
    {
        // is it not task's exception?
        if (!task.IsCanceled && !task.IsFaulted)
            throw; // re-throw

        // swallow, but do not remove the faulted/cancelled task from _pendingTasks 
        // the error will be observed later, when we process _pendingTasks,
        // e.g.: await Task.WhenAll(_pendingTasks.ToArray())
        return;
    }

    // remove the successfully completed task from the list
    lock (_syncLock)
        _pendingTasks.Remove(task);
}

您会这样称呼它:

public Task ProcessAsync()
{
    QueueTaskAsync(SimulateWork());
}

我们的目标是抛出致命异常(例如,出内存不足)立即对当前线程的同步上下文,而任务结果/错误处理被推迟到合适的。

The goal is to throw fatal exceptions (e.g., out-of-memory) immediately on the current thread's synchronization context, while task result/error processing is deferred until appropriate.

有许多人使用任务,发射后忘记这里的一个有趣的讨论。

There's been an interesting discussion of using tasks with fire-and-forget here.

这篇关于不必要的异步/等待时,等待是最后一次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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