我认为使用Task.Factory.StartNew初始化的带有已取消令牌的任务不应该被调度、运行,并且应该以已取消的状态结束。但是,在.NET 5上的VS 2019年中,我注意到很少有任务具有运行状态。
在这种情况下,显然没有执行任务内部的操作。因为在我的示例中,1没有输出到控制台。有人能解释一下到底发生了什么吗?
问题.为什么这个例子中的任务有时运行状态,尽管它显然不工作?
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}");
}
}
}发布于 2022-03-31 14:13:28
在我的机器上,您的代码从未达到Running状态,无论是Framework4.7.2还是.NET 6 (VS 2019)都是如此。正如@JonasH建议的那样,我看了一下代码。下面是一个可能的解释。
在下面的代码中,ExecuteEntryUnsafe由TaskScheduler (这里是ThreadPoolTaskScheduler)在任务计划运行后调用。我尝试使用一个基本的自定义调度程序,在这种情况下,调用的方法是带有一些附加安全性的ExecuteEntry(),以防止双重调用。但这一解释仍然有效。
免责声明:我远没有像Stephen这样的人的名声和知识,所以请谨慎地接受这个答案。如果这不对,我就把它删除。
//---------
// 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();
}
}
}https://stackoverflow.com/questions/71688073
复制相似问题