简体   繁体   English

当多个并行线程等待随后抛出的同一Task实例时会发生什么?

[英]What happens when multiple parallel threads await the same Task instance which then throws?

Reading through answers to this question prompted me to think about what happens exception-wise when the awaited task throws. 通读这个问题的答案,促使我思考当等待的任务抛出时异常情况会如何发生。 Do all "clients" get to observe the exception? 所有“客户”都能观察到异常吗? I admit I may be confusing a couple of things here; 我承认我可能会在这里混淆两件事。 that's the reason I'm asking for clarification. 这就是我要求澄清的原因。

I'll present a concrete scenario... Let's say I have a server with a global collection of long-running Task instances, started by clients. 我将给出一个具体的场景...假设我有一台服务器,其中包含由客户端启动的长期运行的Task实例的全局集合。 After starting one or more tasks, a client can query their progress and retrieve results when they become available, as well as any errors that might occur. 启动一项或多项任务后,客户端可以查询其进度并在可用时检索结果以及可能发生的任何错误。

Tasks themselves may perform very different business-specific things - normally, no two are quite the same. 任务本身可以执行非常不同的业务特定的事情-通常,没有两个是完全相同的。 However, if one of the clients does attempt to start the same task as another had started previously, the server should recognize this and "attach" the second client to the existing task instead of spooling up a new copy. 但是,如果其中一个客户端确实尝试启动与先前已启动的另一个客户端相同的任务,则服务器应识别出该问题并将第二个客户端“附加”到现有任务,而不是假脱机处理新副本。

Now, each time any client queries the status of the task it's interested in, it does something along these lines: 现在,任何客户端每次查询其感兴趣的任务的状态时,都会按照以下步骤进行操作:

var timeout = Task.Delay(numberOfSecondsUntilClientsPatienceRunsOut);
try
{
    var result = await Task.WhenAny(timeout, task);
    if (result == timeout)
    {
        // SNIP: No result is available yet. Just report the progress so far.
    }

    // SNIP: Report the result.
}
catch (Exception e)
{
    // SNIP: Report the error.
}

In short, it gives the task some reasonable time to finish what it's doing first, then falls back to reporting the on-going progress. 简而言之,它为任务提供了一些合理的时间来首先完成其工作,然后退回报告正在进行的进度。 This means there's a potentially significant window of time in which multiple clients could observe the same task failing. 这意味着可能存在很长的时间窗口,在此时间内多个客户端可能会观察到同一任务失败。

My question is: if the task happens to throw during this window, is the exception observed (and handled) by all clients? 我的问题是:如果任务恰好在此窗口期间抛出,所有客户端是否都观察到(并处理了)异常?

Task.WhenAny will not throw itself. Task.WhenAny不会自行抛出。 Per the documentation : 根据文档

The returned task will complete when any of the supplied tasks has completed. 当提供的任何任务完成时,返回的任务将完成。 The returned task will always end in the RanToCompletion state with its Result set to the first task to complete. 返回的任务将始终以RanToCompletion状态结束,其Result设置为要完成的第一个任务。 This is true even if the first task to complete ended in the Canceled or Faulted state. 即使第一个要完成的任务以“ Canceled或“ Faulted状态结束,也是如此。

You will get back either timeout or task . 您将返回timeouttask

If the result is task , and you await that (or get Task.Result ), and task has faulted, then that will throw. 如果结果为task ,并且您等待该结果(或获取Task.Result ),并且task发生故障,则将抛出该错误。 It doesn't matter how many callers do that, or when they do it -- attempting to get the result of a faulted task always throws. 无论有多少调用者执行此操作,或何时执行此操作都无关紧要-总是尝试获取错误任务的结果。 Simple code to demonstrate this: 简单的代码演示这一点:

var t = Task.Run(() => throw new Exception(DateTime.Now.Ticks.ToString()));
try {
    await t;
} catch (Exception e) {
    Console.WriteLine(e.Message);
}
await Task.Delay(1000);
try {
    await t;
} catch (Exception e) {
    Console.WriteLine(e.Message);
}

This will print the same timestamp, twice. 这将打印相同的时间戳两次。 The task only runs once, and only has one result, which produces an exception every time you try to get it. 该任务只运行一次,只有一个结果,每次尝试获取它都会产生一个异常。 If you like you can mix in different threads or parallel calls, this doesn't change the outcome. 如果您喜欢可以混入不同的线程或并行调用,则不会改变结果。

Note that in the case of a timeout, there is still the basic possibility of a race condition: two different tasks/threads, both awaiting the same task, may get different results on await Task.WhenAny(timeout, task) , based on which task they observe to complete first. 请注意,在超时的情况下,仍然存在争用条件的基本可能性:等待同一任务的两个不同的任务/线程在await Task.WhenAny(timeout, task)可能会得到不同的结果。他们观察要首先完成的任务。 In other words, even if await Task.WhenAny(timeout, task) == timeout , task can still have faulted at any point between .WhenAny() deciding it was done and control eventually returning to you. 换句话说,即使await Task.WhenAny(timeout, task) == timeouttask仍然可能在.WhenAny()确定完成与控制权最终返回给您之间的任何时间发生故障。 This is to be expected, and your code should handle this (on the next round of waiting, .WhenAny() would return immediately with the faulted task). 这是可以预期的,并且您的代码应处理此问题(在下一轮等待中, .WhenAny()将与错误的任务一起立即返回)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM