我正在尝试了解.net任务的行为,当孩子被附加时。
我有以下测试代码:
void Test()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task child = null;
var parent = Task.Factory.StartNew(() =>
{
child = Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
}, token);
Thread.Sleep(500);
Debug.WriteLine("State of parent before cancel is {0}", parent.Status);
Debug.WriteLine("State of child before cancel is {0}", child.Status);
tokenSource.Cancel();
Thread.Sleep(500);
Debug.WriteLine("State of parent is {0}", parent.Status);
Debug.WriteLine("State of child is {0}", child.Status);
}
结果是:
State of parent before cancel is WaitingForChildrenToComplete
State of child before cancel is Running
A first chance exception of type 'System.OperationCanceledException' occurred in mscorlib.dll
State of parent is RanToCompletion
State of child is Canceled
尽管如此,父任务状态仍然不是Canceled
两个任务共享令牌,并附加子。
如果发生取消,如何让父任务返回状态Canceled
?
注意
如果我抛出异常,则两个任务都返回Faulted
。
答案 0 :(得分:4)
这是MSDN上所述的预期行为。父任务必须为wait(向下滚动到取消部分)以执行子任务。父任务必须处理所有良性故障(如取消)。
要使您的父任务失败,只需等待并传递令牌:
Task child = null;
var parent = Task.Factory.StartNew(() =>
{
child = Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested) Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
// This is the magic line.
child.Wait(token);
}, token);
如果您使用此代码执行高效工作而不仅仅用于测试,则还应考虑使用支持Task.Run()
代理而不是async
的简化Task.Factory.StartNew()
。这个article非常有趣。
答案 1 :(得分:1)
你的例子很复杂,隐藏了直观的行为,你的期望是错误的。
让我们从工作示例开始:
void Test()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task child = null;
var parent = Task.Factory.StartNew(() =>
{
child = Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
while (!token.IsCancellationRequested)
Thread.Sleep(100);
token.ThrowIfCancellationRequested();
}, token);
Thread.Sleep(500);
Debug.WriteLine("State of parent before cancel is {0}", parent.Status);
Debug.WriteLine("State of child before cancel is {0}", child.Status);
tokenSource.Cancel();
Thread.Sleep(500);
Debug.WriteLine("State of parent is {0}", parent.Status);
Debug.WriteLine("State of child is {0}", child.Status);
}
要取消父级,您需要在父级token.ThrowIfCancellationRequested()
的主体中调用。但是,仅调用token.ThrowIfCancellationRequested()
是不够的。
您需要概念化parent
和child
执行流程是如何组合在一起的,以便了解您的期望为何错误。
Main thread: ---\------------------------------------[Cancel]-----/
Parent: \---\-----[Check cancellation]------------------/
Child: \------------------------------[Cancel]---/
从上图中可以看出,父母在请求取消之前检查取消方式。孩子收到取消信号,因为它基本上等待触发取消。现在,如果你在父母中放置相同的机制,它将收到取消信号,因为在取消信号之前它不会完成它的工作。
答案 2 :(得分:0)
当附加的子任务取消时
文档的standard version指出您需要等待父任务。
当我试图在主线程中等待parentTask.Wait()时-没有错误。
old one“等待父任务”。
当我尝试在parentTask中等待childTask.Wait()然后在主线程中等待parentTask.Wait()时-我遇到了错误。
因此,当前文档有误导性。 另一方面,默认情况下,父任务应等待所有附加的子任务。所以我不明白为什么我应该显式地等待parentTask中的childTask.Wait()来捕获主线程中的TaskCanceledException。