我認為如果(來源)設定了任務的取消狀態:
當令牌處于信號狀態時,任務通過
OperationCanceledException自己拋出一個來確認取消,或者在任務開始執行之前任務已經發出信號。CancellationTokenCancellationToken
但是,如果您ThrowIfCancellationRequested在沒有使用令牌初始化的情況下在任務中呼叫 lambda,則狀態將更改為已取消。這是一個錯誤還是請解釋發生了什么?
在 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
uj5u.com熱心網友回復:
這可能是 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)or if (true)orfor (; ; )塊內拋出,會觸發異步決議。
Task.Run(Func<Task>)多載導致 aCanceled而不是Faulted任務的原因是Task.Run(Func<Task>) ,本質上是一個捷徑:
Task.Factory.StartNew<Task>(function,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default).Unwrap();
當function是一個async方法時,它總是成功地產生一個Task. 該任務被傳遞給Unwrap,它按原樣傳播其狀態(CancellationToken沒有傳遞給Unwrap)。當async遇到OperationCanceledException. 這兩個因素的組合導致觀察到的不一致行為。這種行為可能是疏忽而不是故意的。但由于Task.Run它已經生產了 10 年,現在改變它可能為時已晚。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/457408.html
