[英]What happens when multiple parallel threads await the same Task instance which then throws?
通读这个问题的答案,促使我思考当等待的任务抛出时异常情况会如何发生。 所有“客户”都能观察到异常吗? 我承认我可能会在这里混淆两件事。 这就是我要求澄清的原因。
我将给出一个具体的场景...假设我有一台服务器,其中包含由客户端启动的长期运行的Task
实例的全局集合。 启动一项或多项任务后,客户端可以查询其进度并在可用时检索结果以及可能发生的任何错误。
任务本身可以执行非常不同的业务特定的事情-通常,没有两个是完全相同的。 但是,如果其中一个客户端确实尝试启动与先前已启动的另一个客户端相同的任务,则服务器应识别出该问题并将第二个客户端“附加”到现有任务,而不是假脱机处理新副本。
现在,任何客户端每次查询其感兴趣的任务的状态时,都会按照以下步骤进行操作:
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.
}
简而言之,它为任务提供了一些合理的时间来首先完成其工作,然后退回报告正在进行的进度。 这意味着可能存在很长的时间窗口,在此时间内多个客户端可能会观察到同一任务失败。
我的问题是:如果任务恰好在此窗口期间抛出,所有客户端是否都观察到(并处理了)异常?
Task.WhenAny
不会自行抛出。 根据文档 :
当提供的任何任务完成时,返回的任务将完成。 返回的任务将始终以
RanToCompletion
状态结束,其Result设置为要完成的第一个任务。 即使第一个要完成的任务以“Canceled
或“Faulted
状态结束,也是如此。
您将返回timeout
或task
。
如果结果为task
,并且您等待该结果(或获取Task.Result
),并且task
发生故障,则将抛出该错误。 无论有多少调用者执行此操作,或何时执行此操作都无关紧要-总是尝试获取错误任务的结果。 简单的代码演示这一点:
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);
}
这将打印相同的时间戳两次。 该任务只运行一次,只有一个结果,每次尝试获取它都会产生一个异常。 如果您喜欢可以混入不同的线程或并行调用,则不会改变结果。
请注意,在超时的情况下,仍然存在争用条件的基本可能性:等待同一任务的两个不同的任务/线程在await Task.WhenAny(timeout, task)
可能会得到不同的结果。他们观察要首先完成的任务。 换句话说,即使await Task.WhenAny(timeout, task) == timeout
, task
仍然可能在.WhenAny()
确定完成与控制权最终返回给您之间的任何时间发生故障。 这是可以预期的,并且您的代码应处理此问题(在下一轮等待中, .WhenAny()
将与错误的任务一起立即返回)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.