当仍在运行

时间:2016-03-04 16:12:15

标签: c# asynchronous async-await

我仍然在使用async& amp;多线程。我正在尝试监视“任务启动”何时仍在运行(在UI中显示)。然而,它表明RanToCompletion比我想要的更早,当它遇到等待时,即使我认为它的状态仍在运行。

以下是我正在做的样本。这一切似乎都集中在await的周围。当它命中等待时,它会被标记为RanToCompletion。

我希望跟踪主要任务,它会以一种方式向我表明它仍然在运行一直到最后并且完成所有操作后只有RanToCompletion,包括repo调用和WhenAll。

如何更改此设置以获取有关tskProdSeeding任务状态的反馈?

我的控制台应用程序主方法调用:

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);
    }
}

3 个答案:

答案 0 :(得分:2)

如果使用async void外部任务无法判断任务何时完成,则需要使用async Task

其次,一旦切换到async TaskTask.Factory.StartNew无法处理返回Task的函数,您需要切换到Task.Run(

tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);

一旦你完成了这两项修改,你就可以等待.Wait() tskProdSeeding async void并等待所有工作完成后再继续。

请阅读“Async/Await - Best Practices in Asynchronous Programming”以了解有关不执行SeedingProd的更多信息。

请阅读“StartNew is Dangerous”,了解有关不应该按照使用方式使用StartNew的原因。

P.S。在await Task.Delay(30000);中,您应该将其切换为使用Thread.Sleep(30000);的{​​{1}} insetad,然后在等待时不会占用线程。如果你这样做,你可能会放弃

tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);

然后点亮

tskProdSeeding = SeedingProd(_cts.Token);

因为函数不再具有阻塞调用。

答案 1 :(得分:1)

我不相信你需要第二个线程(Task.RunStartNew)。看起来大部分工作都是I / O绑定的,如果你是异步地使用Task.Delay而不是Thread.Sleep,那么这些操作和你的UI会消耗there is no thread不应该冻结。任何对异步新手都需要了解的第一件事就是它与多线程不同。后者是关于消耗更多线程,前者是消耗更少的线程。专注于消除阻止,你不需要第二个线程。

正如其他人所说,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);

答案 2 :(得分:0)

当您在方法上指定async时,它会编译为具有任务的状态机,因此SeedingProd不会同步运行,但即使返回void也会充当任务。因此,当您致电Task.Factory.StartNew(SeedingProd)时,您启动了启动另一项任务的任务 - 这就是为什么第一个任务在第二个任务之前完成的原因。您所要做的就是添加Task返回参数而不是void

private async Task SeedingProdAsync(CancellationToken ct)
{
...
}

并简单地将其称为:

Task tskProdSeeding = SeedingProdAsync(_cts.Token);