Đoạn mã sau tạo một tác vụ đang bị hủy. Biểu thức await
(trường hợp 1) ném System.OperationCanceledException
trong khi đồng bộ Wait()
(trường hợp 2) ném System.Threading.Tasks.TaskCanceledException
(được bọc trong System.AggregateException
).OperationCanceledException VS TaskCanceledException khi tác vụ bị hủy
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
Program.MainAsync().Wait();
}
private static async Task MainAsync()
{
using(var cancellationTokenSource = new CancellationTokenSource())
{
var token = cancellationTokenSource.Token;
const int cancelationCheckTimeout = 100;
var task = Task.Run(
async() =>
{
for (var i = 0; i < 100; i++)
{
token.ThrowIfCancellationRequested();
Console.Write(".");
await Task.Delay(cancelationCheckTimeout);
}
},
cancellationTokenSource.Token
);
var cancelationDelay = 10 * cancelationCheckTimeout;
cancellationTokenSource.CancelAfter(cancelationDelay);
try
{
await task; // (1)
//task.Wait(); // (2)
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}");
Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}");
Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}");
}
}
}
}
Trường hợp 1 đầu ra:
..........System.OperationCanceledException: The operation was canceled.
at System.Threading.CancellationToken.ThrowIfCancellationRequested()
at Program.<>c__DisplayClass1_0.<<MainAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Program.<MainAsync>d__1.MoveNext()
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null
Trường hợp 2 đầu ra:
..........System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled.
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Program.<MainAsync>d__1.MoveNext()
---> (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.<---
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null
Tại sao System.AggregateException
trong trường hợp thứ 2 không chứa System.OperationCanceledException
là một ngoại lệ bên trong?
Tôi biết rằng ThrowIfCancellationRequested()
ném OperationCanceledException
và chúng tôi có thể thấy rằng trong cả hai trường hợp Task
sẽ bị hủy (không bị lỗi).
này câu đố tôi vì hủy một phương pháp từ API .NET tạo ra hành vi phù hợp trong cả hai trường hợp - nhiệm vụ hủy chỉ TaskCanceledException
ném:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
Program.MainAsync().Wait();
}
private static async Task MainAsync()
{
using(var cancellationTokenSource = new CancellationTokenSource())
{
var token = cancellationTokenSource.Token;
var task = Task.Delay(1000, token);
cancellationTokenSource.CancelAfter(100);
try
{
await task; // (1)
//task.Wait(); // (2)
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}");
Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}");
Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}");
}
}
}
}
Trường hợp 1 đầu ra:
System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Program.<MainAsync>d__1.MoveNext()
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null
Trường hợp 2 đầu ra:
System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled.
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Program.<MainAsync>d__1.MoveNext()
---> (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.<---
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null
Có quan trọng không? 'TaskCanceledException' là một lớp dẫn xuất của' OperationCanceledException' vì vậy nếu bạn đã nắm bắt 'catch (OperationCanceledException e)', bạn sẽ bắt cả hai loại ngoại lệ. Phần thông tin duy nhất bạn mất là thuộc tính 'TaskCanceledException.Task'. –
Tôi đồng ý nhưng tôi tò mò hơn về lý do giới thiệu/sử dụng 'TaskCanceledException' khi đã có' OperationCanceledException'. –
Lý do để giới thiệu nó là khi bạn có một số trường hợp, bạn có thể lấy bối cảnh của Tác vụ đã bị hủy trong những trường hợp bạn có thể ném biểu mẫu có nguồn gốc hơn bao gồm Tác vụ.'ThrowIfCancellationRequested' được viết chung, nó không biết nó nằm bên trong một Task nên nó làm tăng ngoại lệ tổng quát hơn mà không có thuộc tính Task. –