为什么这个测试失败?
据我所知,t1
和t2
之间的唯一区别是t1
是Task
,t2
是Task<int>
}。但由于某种原因,t2
最终处于Faulted
状态,而不是Canceled
状态。为什么行为会有所不同?
[Test]
public void Test_foo()
{
var t1 = Task.Run(() =>
{
throw new OperationCanceledException();
});
try
{
t1.Wait();
}
catch (AggregateException e)
{
Assert.IsTrue(t1.IsCanceled);
}
var t2 = Task.Run(() =>
{
throw new OperationCanceledException();
return 1;
});
try
{
t2.Wait();
}
catch (AggregateException e)
{
Assert.IsTrue(t2.IsCanceled); // fails, it's Faulted
}
}
答案 0 :(得分:2)
您的任务之间的主要区别在于您正在使用的Task.Run
方法的重载:
task1
是使用Task.Run Method (Func<Task>)
创建的,而task2
是使用Task.Run<TResult> Method (Func<TResult>)
创建的。这种重载确实创造了一点点差异:
task1
Result
属性设为System.Threading.Tasks.VoidTaskResult
,CreationOptions
设为None
,task2
CreationOptions
设置为DenyChildAttach
,结果为default(int)
,即0
。当您等待task2
时,Result
属性未设置为实际值,因为抛出了异常。根据{{3}}:
当任务实例观察到引发的MSDN时 用户代码,它将异常的令牌与其关联的令牌进行比较 (传递给创建
Task
的API的那个)。如果他们 是相同的,令牌的OperationCanceledException
属性返回 true ,该任务将此解释为确认取消和 转换到Canceled
状态。如果您不使用IsCancellationRequested
或Wait
等待任务的方法,然后任务只是将其状态设置为Canceled
。如果您正在等待转换为的任务
Canceled
州,WaitAll
抛出异常(包含在System.Threading.Tasks.TaskCanceledException
异常中)。注意 此异常表示成功取消而不是 错误的情况。因此,任务的AggregateException
属性返回 的空强>如果令牌的
Exception
属性返回false
或 如果异常的令牌与任务的令牌不匹配,则IsCancellationRequested
被视为正常异常,导致 要转换到Faulted
状态的任务。还要注意 存在其他异常也会导致Task
转换为Faulted
州。Equals
。您可以在中获取已完成任务的状态OperationCanceledException
财产。
所以,在这里我们可以找到这种行为的原因 - 由于令牌不匹配,异常被视为正常异常。这很奇怪,因为令牌是Status
(我在Debug中检查过,哈希码相等,true
方法和double等于运算符返回false
),但比较仍然返回var t1TokenSource = new CancellationTokenSource();
var t1 = Task.Run(() =>
{
Thread.Sleep(1000);
if (t1TokenSource.Token.IsCancellationRequested)
{
t1TokenSource.Token.ThrowIfCancellationRequested();
}
//throw new TaskCanceledException();
}, t1TokenSource.Token);
try
{
t1TokenSource.Cancel();
t1.Wait();
}
catch (AggregateException e)
{
Debug.Assert(t1.IsCanceled);
}
var t2TokenSource = new CancellationTokenSource();
var t2 = Task.Run(() =>
{
Thread.Sleep(1000);
if (t2TokenSource.Token.IsCancellationRequested)
{
t2TokenSource.Token.ThrowIfCancellationRequested();
}
//throw new TaskCanceledException();
return 1;
}, t2TokenSource.Token);
try
{
t2TokenSource.Cancel();
t2.Wait();
}
catch (AggregateException e)
{
Debug.Assert(t2.IsCanceled);
}
。所以,你的案例的解决方案是明确使用definitely the same,就像这样(我添加了cancellation tokens以避免竞争条件):
TaskStatus.RanToCompletion
来自Thread.Sleep
的另一句话:
您可以使用以下选项之一终止操作:
- 只需从代表处返回即可。在许多情况下,这已足够;但是,以这种方式取消的任务实例 转换到
TaskStatus.Canceled
状态,而不是转换为Canceled
州。- 投掷MSDN并传递请求取消的令牌。 这样做的首选方法是 使用
OperationCanceledException
方法。一项任务 以这种方式取消过渡到task
状态,其中 调用代码可以用来验证任务是否响应了它 取消请求。
正如您所看到的,preffered方式是可预测的,直接异常抛出没有。另请注意,如果使用情况DenyChildAttach
也是//int arrayOverEightPounds[];
List<Integer> listOverEightPounds = new ArrayList<>();
//...
//arrayOverEightPounds[k] = array[i];
listOverEightPounds.add(array[i]);
//....
return listOverEightPounds.toArray(new Integer[0]);
创建的,并且没有ThrowIfCancellationRequested
属性,那么构建器中存在一些差异,您已经遇到过。
希望这会有所帮助。