在没有使用令牌初始化和在 lambda 中调用 ThrowIfCancellationRequested 的情况下,任务中的状态被取消
Status is canceled in task without initialization with token and with ThrowIfCancellationRequested call in lambda
我认为如果(source):
设置了任务的取消状态
The task acknowledged cancellation by throwing an OperationCanceledException
with its own CancellationToken
while the token was in signaled state, or the task's CancellationToken
was already signaled before the task started executing.
但是如果你在任务的 lambda 中调用 ThrowIfCancellationRequested
而没有用令牌初始化,那么状态会变为已取消。
这是一个错误还是请解释这是怎么回事?
在 C# 上针对 VS 2019 上的 .NET 5 和 VS 2022 上的 .NET 6 进行了测试。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(500);
void MyMethod()
{
while (true)
{
Thread.Sleep(100);
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
}
Task t1 = new Task(MyMethod);
t1.Start();
Task<int> t2 = new Task<int>(() => { MyMethod(); return 1; });
t2.Start();
Task t3 = Task.Factory.StartNew(MyMethod);
Task t4 = Task.CompletedTask.ContinueWith((t) => { MyMethod(); });
Task t5 = Task.Run(MyMethod);
Task t6 = Task.Run(() =>
{
while (true)
{
Thread.Sleep(100);
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
});
try
{
Task.WaitAll(t1, t2, t3, t4, t5, t6);
}
catch
{
}
Console.WriteLine($"Status t1 from new Task: {t1.Status}"); //Faulted
Console.WriteLine($"Status t2 from new Task<: {t2.Status}"); //Faulted
Console.WriteLine($"Status t3 from Task.Factory.StartNew: {t3.Status}"); //Faulted
Console.WriteLine($"Status t4 from Task.CompletedTask.ContinueWith: {t4.Status}"); //Faulted
Console.WriteLine("\n");
Console.WriteLine($"Status t5 from Task.Run: {t5.Status}"); //Faulted
Console.WriteLine($"Status t6 from Task.Run with lambda: {t6.Status}"); //Canceled
}
}
}
Faulted
而不是 Canceled
.
输出:
Status t1 from new Task: Faulted
Status t2 from new Task<int>: Faulted
Status t3 from Task.Factory.StartNew: Faulted
Status t4 from Task.CompletedTask.ContinueWith: Faulted
Status t5 from Task.Run: Faulted
Status t6 from Task.Run with lambda: Canceled
这可能是 C# 编译器中的错误。最小复制:
using System;
using System.Threading.Tasks;
static class Program
{
static async Task Main()
{
{
var task = Task.Run(() => // Resolved to Task.Run(Action)
{
bool condition = true;
if (condition) throw new OperationCanceledException();
});
try { await task; } catch { }
Console.WriteLine($"if (condition): {task.Status}");
}
{
var task = Task.Run(() => // Resolved to Task.Run(Func<Task>)
{
if (true) throw new OperationCanceledException();
});
try { await task; } catch { }
Console.WriteLine($"if (true): {task.Status}");
}
}
}
输出:
if (condition): Faulted
if (true): Canceled
Task.Run
有许多重载,其中一个带有 Action
参数,一个带有 Func<Task>
参数。似乎无条件地在 lambda 中的 while (true)
或 if (true)
或 for (; ; )
块内抛出,会触发异步解析。
Task.Run(Func<Task>)
重载导致 Canceled
而不是 Faulted
任务的原因是因为 Task.Run(Func<Task>)
is essentially a shortcut 为此:
Task.Factory.StartNew<Task>(function,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default).Unwrap();
当 function
是一个 async
方法时,它总能成功生成 Task
。此任务被传递给 Unwrap
,后者按原样传播其状态(CancellationToken
未传递给 Unwrap
)。此外,async
方法在遇到 OperationCanceledException
时会按设计转换为取消状态。这两个因素的组合导致观察到的不一致行为。这种行为可能是疏忽而不是故意的。但由于 Task.Run
已经生产了 10 年,现在更改它可能为时已晚。
我认为如果(source):
设置了任务的取消状态The task acknowledged cancellation by throwing an
OperationCanceledException
with its ownCancellationToken
while the token was in signaled state, or the task'sCancellationToken
was already signaled before the task started executing.
但是如果你在任务的 lambda 中调用 ThrowIfCancellationRequested
而没有用令牌初始化,那么状态会变为已取消。
这是一个错误还是请解释这是怎么回事?
在 C# 上针对 VS 2019 上的 .NET 5 和 VS 2022 上的 .NET 6 进行了测试。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(500);
void MyMethod()
{
while (true)
{
Thread.Sleep(100);
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
}
Task t1 = new Task(MyMethod);
t1.Start();
Task<int> t2 = new Task<int>(() => { MyMethod(); return 1; });
t2.Start();
Task t3 = Task.Factory.StartNew(MyMethod);
Task t4 = Task.CompletedTask.ContinueWith((t) => { MyMethod(); });
Task t5 = Task.Run(MyMethod);
Task t6 = Task.Run(() =>
{
while (true)
{
Thread.Sleep(100);
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
});
try
{
Task.WaitAll(t1, t2, t3, t4, t5, t6);
}
catch
{
}
Console.WriteLine($"Status t1 from new Task: {t1.Status}"); //Faulted
Console.WriteLine($"Status t2 from new Task<: {t2.Status}"); //Faulted
Console.WriteLine($"Status t3 from Task.Factory.StartNew: {t3.Status}"); //Faulted
Console.WriteLine($"Status t4 from Task.CompletedTask.ContinueWith: {t4.Status}"); //Faulted
Console.WriteLine("\n");
Console.WriteLine($"Status t5 from Task.Run: {t5.Status}"); //Faulted
Console.WriteLine($"Status t6 from Task.Run with lambda: {t6.Status}"); //Canceled
}
}
}
Faulted
而不是 Canceled
.
输出:
Status t1 from new Task: Faulted
Status t2 from new Task<int>: Faulted
Status t3 from Task.Factory.StartNew: Faulted
Status t4 from Task.CompletedTask.ContinueWith: Faulted
Status t5 from Task.Run: Faulted
Status t6 from Task.Run with lambda: Canceled
这可能是 C# 编译器中的错误。最小复制:
using System;
using System.Threading.Tasks;
static class Program
{
static async Task Main()
{
{
var task = Task.Run(() => // Resolved to Task.Run(Action)
{
bool condition = true;
if (condition) throw new OperationCanceledException();
});
try { await task; } catch { }
Console.WriteLine($"if (condition): {task.Status}");
}
{
var task = Task.Run(() => // Resolved to Task.Run(Func<Task>)
{
if (true) throw new OperationCanceledException();
});
try { await task; } catch { }
Console.WriteLine($"if (true): {task.Status}");
}
}
}
输出:
if (condition): Faulted
if (true): Canceled
Task.Run
有许多重载,其中一个带有 Action
参数,一个带有 Func<Task>
参数。似乎无条件地在 lambda 中的 while (true)
或 if (true)
或 for (; ; )
块内抛出,会触发异步解析。
Task.Run(Func<Task>)
重载导致 Canceled
而不是 Faulted
任务的原因是因为 Task.Run(Func<Task>)
is essentially a shortcut 为此:
Task.Factory.StartNew<Task>(function,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default).Unwrap();
当 function
是一个 async
方法时,它总能成功生成 Task
。此任务被传递给 Unwrap
,后者按原样传播其状态(CancellationToken
未传递给 Unwrap
)。此外,async
方法在遇到 OperationCanceledException
时会按设计转换为取消状态。这两个因素的组合导致观察到的不一致行为。这种行为可能是疏忽而不是故意的。但由于 Task.Run
已经生产了 10 年,现在更改它可能为时已晚。