任务在等待时标记为 RanToComplete,但仍在运行
本文关键字:运行 RanToComplete 在等待 记为 任务 | 更新日期: 2023-09-27 18:37:28
我仍然在跟上异步和多线程的速度。我正在尝试监视我启动的任务何时仍在运行(以在 UI 中显示)。 但是,它表明它比我想要的更早是 RanToCompletion,当它进入等待状态时,即使我认为它的状态仍在运行。
这是我正在做的示例。 这一切似乎都围绕着等待者。 当它命中等待时,它被标记为 RanToCompletion。
我想跟踪启动所有内容的主要任务,在某种程度上向我表明它仍然一直运行到最后,并且只有在完成所有操作时才能运行到 RanToComplete,包括 repo 调用和 WhenAll。
如何更改此设置以获取有关 tskProdSeeding 任务状态的反馈?
我的控制台应用程序 Main 方法调用以下内容:
Task tskProdSeeding;
tskProdSeeding = Task.Factory.StartNew(SeedingProd, _cts.Token);
运行这个:
private async void SeedingProd(object state)
{
var token = (CancellationToken)state;
while (!token.IsCancellationRequested)
{
int totalSeeded = 0;
var codesToSeed = await _myRepository.All().ToListAsync(token);
await Task.WhenAll(Task.Run(async () =>
{
foreach (var code in codesToSeed)
{
if (!token.IsCancellationRequested)
{
try
{
int seedCountByCode = await _myManager.SeedDataFromLive(code);
totalSeeded += seedCountByCode;
}
catch (Exception ex)
{
_logger.InfoFormat(ex.ToString());
}
}
}
}, token));
Thread.Sleep(30000);
}
}
如果使用async void
则外部任务无法判断任务何时完成,则需要改用async Task
。
其次,一旦你切换到async Task
,Task.Factory.StartNew
无法处理返回Task
的函数,你需要切换到Task.Run(
tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);
完成这两项更改后,您将能够等待或.Wait()
tskProdSeeding
并且它会正确等待所有工作完成后再继续。
请阅读"异步/等待 - 异步编程中的最佳实践"以了解有关不执行async void
的更多信息。
请阅读"StartNew is Dangerous",以了解有关为什么您不应该以使用StartNew的方式的更多信息。
附言SeedingProd
您应该将其切换为使用 Thread.Sleep(30000);
的插入await Task.Delay(30000);
,然后您将不会在等待线程时捆绑线程。如果您这样做,您可能会删除
tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);
然后就去做
tskProdSeeding = SeedingProd(_cts.Token);
因为该函数内部不再有阻塞调用。
我不相信你根本不需要第二个线程(Task.Run
或StartNew
)。看起来大部分工作都是 I/O 绑定的,如果您异步执行此操作并使用 Task.Delay
而不是 Thread.Sleep
,那么这些操作不会消耗线程,您的 UI 不应冻结。任何刚接触异步的人都需要了解的第一件事是,它与多线程不是一回事。后者是消耗更多的线程,前者是消耗更少的线程。专注于消除阻塞,您不需要第二个线程。
正如其他人所指出的,SeedingProd
需要返回一个Task
,而不是void
,所以你可以观察它的完成。我相信你的方法可以简化为:
private async Task SeedingProd(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
int totalSeeded = 0;
var codesToSeed = await _myRepository.All().ToListAsync(token);
foreach (var code in codesToSeed)
{
if (token.IsCancellationRequested)
return;
try
{
int seedCountByCode = await _myManager.SeedDataFromLive(code);
totalSeeded += seedCountByCode;
}
catch (Exception ex)
{
_logger.InfoFormat(ex.ToString());
}
}
await Task.Dealy(30000);
}
}
然后只需调用该方法,无需等待它,您就会有您的任务。
Task mainTask = SeedingProd(token);
当您在方法上指定async
时,它会编译到带有 Task 的状态机中,因此SeedingProd
不会同步运行,而是充当 Task 即使返回 void
。因此,当你调用Task.Factory.StartNew(SeedingProd)
时,你开始了一个启动另一个任务的任务 - 这就是为什么第一个任务在第二个任务之前立即完成。您所要做的就是添加 Task
返回参数而不是 void
:
private async Task SeedingProdAsync(CancellationToken ct)
{
...
}
并像这样简单地称呼它:
Task tskProdSeeding = SeedingProdAsync(_cts.Token);