Task.Factory.StartNew 已取消令牌的状态为 运行
Task.Factory.StartNew with a canceled token has the status running
我认为不应安排使用 Task.Factory.StartNew 和已取消标记初始化的任务 运行,并且应该以已取消状态结束。但是在 .NET 5 上的 VS 2019 中,我注意到很少有任务具有 运行ning 状态。
在这种情况下,显然没有执行任务内部的操作。因为在我的例子中 1 并没有输出到控制台。谁能解释一下到底发生了什么?
问题。为什么本例中的任务有时状态为运行ning,虽然它显然不起作用?
UPD。在 .NET 6 上检查 VS 2022。同样的事情。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(1);
Thread.Sleep(100);
Task t1 = new Task(() => { }, cancellationTokenSource.Token);
Task t2 = new Task(() => { }, cancellationTokenSource.Token);
Task t3 = new Task(() => { }, cancellationTokenSource.Token);
Task t4 = new Task(() => { }, cancellationTokenSource.Token);
Task t5 = new Task(() => { }, cancellationTokenSource.Token);
Console.WriteLine($"t1: {t1.Status}");
Console.WriteLine($"t2: {t2.Status}");
Console.WriteLine($"t3: {t3.Status}");
Console.WriteLine($"t4: {t4.Status}");
Console.WriteLine($"t5: {t5.Status}");
Task t6 = Task.Factory.StartNew(() =>
{
while (true)
{
Console.WriteLine(1);
}
}, cancellationTokenSource.Token);
Console.WriteLine($"t6: {t6.Status}");
Console.WriteLine($"t6: {t6.Status}");
Console.WriteLine($"t6: {t6.Status}");
Console.WriteLine($"t6: {t6.Status}");
}
}
}
在我的机器上,您的代码永远不会达到 Running
状态,无论是使用 Framework 4.7.2 还是使用 .NET 6 (VS 2019)。正如@JonasH 所建议的,我看了一下代码。以下是一种可能的解释。
在下面的代码中,ExecuteEntryUnsafe
在任务安排到 运行 之后由 TaskScheduler
(这里是 ThreadPoolTaskScheduler
)调用。我尝试使用基本的自定义调度程序,在这种情况下,调用的方法是 ExecuteEntry()
和一些额外的安全措施以防止重复调用。但解释仍然有效。
免责声明:我远没有像 Stephen Cleary 这样的人那样享有声誉或知识,所以请谨慎对待这个答案。如果这不正确,我会删除它。
//---------
// Task.cs
//---------
internal void ExecuteEntryUnsafe(Thread? threadPoolThread)
{
// [SOURCE comment] Remember that we started running the task delegate
// This is what actually makes the status show as Running (see source):
// ...
// int sf = m_stateFlags;
//
// if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted;
// else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled;
// else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion;
// else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete;
// else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running; <-- here
// ...
m_stateFlags |= (int)TaskStateFlags.DelegateInvoked;
// ***
// On your machine, execution probably switches back to main thread for a fraction of a second here.
// Hence one of your WriteLines outputs "Running". But this means "delegate was invoked", not necessarily "code is executing".
// On my machine this does not happen, so I do not see Running status.
// ***
if (!IsCancellationRequested & !IsCanceled)
{
ExecuteWithThreadLocal(ref t_currentTask, threadPoolThread);
}
else
{
// As Cancellation is requested this will be executed, even if IsCancelled is false.
ExecuteEntryCancellationRequestedOrCanceled();
}
}
// ...
internal void ExecuteEntryCancellationRequestedOrCanceled()
{
// If Task is cancelled; this is different that "is cancellation requested". So this block is executed.
if (!IsCanceled)
{
// Set Cancelled flag.
int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | (int)TaskStateFlags.Canceled);
// If previous state was not yet Cancelled, perform some cleanup.
if ((prevState & (int)TaskStateFlags.Canceled) == 0)
{
CancellationCleanupLogic();
}
}
}
我认为不应安排使用 Task.Factory.StartNew 和已取消标记初始化的任务 运行,并且应该以已取消状态结束。但是在 .NET 5 上的 VS 2019 中,我注意到很少有任务具有 运行ning 状态。
在这种情况下,显然没有执行任务内部的操作。因为在我的例子中 1 并没有输出到控制台。谁能解释一下到底发生了什么?
问题。为什么本例中的任务有时状态为运行ning,虽然它显然不起作用?
UPD。在 .NET 6 上检查 VS 2022。同样的事情。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(1);
Thread.Sleep(100);
Task t1 = new Task(() => { }, cancellationTokenSource.Token);
Task t2 = new Task(() => { }, cancellationTokenSource.Token);
Task t3 = new Task(() => { }, cancellationTokenSource.Token);
Task t4 = new Task(() => { }, cancellationTokenSource.Token);
Task t5 = new Task(() => { }, cancellationTokenSource.Token);
Console.WriteLine($"t1: {t1.Status}");
Console.WriteLine($"t2: {t2.Status}");
Console.WriteLine($"t3: {t3.Status}");
Console.WriteLine($"t4: {t4.Status}");
Console.WriteLine($"t5: {t5.Status}");
Task t6 = Task.Factory.StartNew(() =>
{
while (true)
{
Console.WriteLine(1);
}
}, cancellationTokenSource.Token);
Console.WriteLine($"t6: {t6.Status}");
Console.WriteLine($"t6: {t6.Status}");
Console.WriteLine($"t6: {t6.Status}");
Console.WriteLine($"t6: {t6.Status}");
}
}
}
在我的机器上,您的代码永远不会达到 Running
状态,无论是使用 Framework 4.7.2 还是使用 .NET 6 (VS 2019)。正如@JonasH 所建议的,我看了一下代码。以下是一种可能的解释。
在下面的代码中,ExecuteEntryUnsafe
在任务安排到 运行 之后由 TaskScheduler
(这里是 ThreadPoolTaskScheduler
)调用。我尝试使用基本的自定义调度程序,在这种情况下,调用的方法是 ExecuteEntry()
和一些额外的安全措施以防止重复调用。但解释仍然有效。
免责声明:我远没有像 Stephen Cleary 这样的人那样享有声誉或知识,所以请谨慎对待这个答案。如果这不正确,我会删除它。
//---------
// Task.cs
//---------
internal void ExecuteEntryUnsafe(Thread? threadPoolThread)
{
// [SOURCE comment] Remember that we started running the task delegate
// This is what actually makes the status show as Running (see source):
// ...
// int sf = m_stateFlags;
//
// if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted;
// else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled;
// else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion;
// else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete;
// else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running; <-- here
// ...
m_stateFlags |= (int)TaskStateFlags.DelegateInvoked;
// ***
// On your machine, execution probably switches back to main thread for a fraction of a second here.
// Hence one of your WriteLines outputs "Running". But this means "delegate was invoked", not necessarily "code is executing".
// On my machine this does not happen, so I do not see Running status.
// ***
if (!IsCancellationRequested & !IsCanceled)
{
ExecuteWithThreadLocal(ref t_currentTask, threadPoolThread);
}
else
{
// As Cancellation is requested this will be executed, even if IsCancelled is false.
ExecuteEntryCancellationRequestedOrCanceled();
}
}
// ...
internal void ExecuteEntryCancellationRequestedOrCanceled()
{
// If Task is cancelled; this is different that "is cancellation requested". So this block is executed.
if (!IsCanceled)
{
// Set Cancelled flag.
int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | (int)TaskStateFlags.Canceled);
// If previous state was not yet Cancelled, perform some cleanup.
if ((prevState & (int)TaskStateFlags.Canceled) == 0)
{
CancellationCleanupLogic();
}
}
}