【问题标题】:Task status changed to RanToCompletion while stille running静止运行时任务状态更改为 RanToCompletion
【发布时间】:2018-10-22 20:56:45
【问题描述】:

我创建了一个方法,它在启动作为参数传递的任务之前处理一些检查。

我的问题是,在那里创建的任务没有按预期运行,尽管代码仍在运行,但很快就会被视为 RanToCompletion。

这是一个例子:

    public Task Main(CancellationToken localToken)
    {
        try
        {
            AddToTasker(new Task(async () => await ExtractAllOffer(localToken), localToken), TaskTypes.Extractor);

            //this allows to extract only the task for the given task type through the list created in AddToTasker, actual code is not necessary the returned array is correct
            Task.WaitAll(GetTasksArray(new TaskTypes[]{TaskTypes.Extractor}), localToken);

            IsRunning = false;
        }
    }

    public void AddToTasker(Task task, TaskTypes taskstype)
    {

        /*
         * Whatever code to perform few check before starting the task
         * among which referencing the task within a list which holds also the taskstype
         */


        task.Start();

    }

    async private Task ExtractAllOffer(CancellationToken localToken)
    {
        // *** Do very long Stuff ***
    }

ExtractAllOffer 方法是一个循环,有一会儿我 await 外部代码完成。在第一个await Task.WaiAll 终止并转到IsRunning = false

我检查了这个thread,但它似乎与我正确使用异步任务而不是异步无效的问题不同。

此外,在我在 AddToTasker 方法中转移任务执行之前,代码可以正常运行。在我以前这样做之前AddToTasker(Task.Run(() => ExtractAllOffer(localToken), localToken), TaskTypes.Extractor);,但我意识到我需要在启动任务之前执行检查,并且需要在 AddToTasker 方法中考虑检查(我对此方法有很多调用)。

我有点理解我声明或启动任务的方式存在缺陷,但不知道是什么。

帮助不胜感激

【问题讨论】:

  • Task 构造函数和Task.Run 的行为确实不同。请参阅此answer。这与您检查的链接相同,但可能解释更清楚。
  • 感谢您的链接,它帮助很大。我有点明白发生了什么,但不知道该怎么做,我给出了正确的答案

标签: task-parallel-library


【解决方案1】:

感谢@pere57 和其他thread 我看到等待只等到创建任务的操作完成......这非常快。

我必须将其声明为 Task,以便解开第一个 Task(动作)以访问内部 Task(实际执行的方法)。

就这样吧:

public Task Main(CancellationToken localToken)
{
    try
    {
        AddToTasker(new Task<Task>(() => ExtractAllOffer(localToken), localToken), TaskTypes.Extractor);

        //this allows to extract only the task for the given task type through the list created in AddToTasker, actual code is not necessary the returned array is correct
        Task.WaitAll(GetTasksArray(new TaskTypes[]{TaskTypes.Extractor}), localToken);

        IsRunning = false;
    }
}

public void AddToTasker(Task<Task> task, TaskTypes taskstype)
{

    /*
     * Whatever code to perform few check before starting the task
     * among which referencing the task within a list which holds also the taskstype
     */

    mylistoftask.Add(task.Unwrap()); //I have now to unwrap the task to add the inner one in my list
    task.Start();

}

async private Task ExtractAllOffer(CancellationToken localToken)
{
    // *** Do very long Stuff ***
}

【讨论】:

  • 你为什么首先使用Task.Start?没有理由使用冷任务。只需使用 Task.Run。也没有理由使用Task&lt;Task&gt;。这就是await 通常所做的。这段代码过于复杂。如果要执行检查,传递的参数应该是要执行的Action,而不是代表其执行的Task。
  • 您想解决的实际问题是什么?可能已经有一个内置的解决方案,例如ActionBlock&lt;T&gt; 可以在一个单独的任务中排队和处理消息 - 无需实现自己的工人类。 Dataflow 命名空间中的其他类可用于创建整个处理管道。 await Task.WhenAll() 可以在不阻塞的情况下等待多个任务
  • 为了简单起见,AddToTasker 方法是一个通用方法,它对任务创建执行一些检查,其中检查边界是否被授权,有多少任务正在运行,......一些Tasks 使用的托管代码在超过 40 个并发任务时变得不稳定,并且从我的系统中吸收了大量资源。在 Task.Run 之前调用检查方法似乎太重了。为了记录,我以前实际上是这样做的,而且一团糟。我自己的工作人员还添加了一些审计/日志数据用于调试目的。
  • 如果前面的代码一团糟,那不是 Task.Run 的错。这意味着验证代码没有正确考虑。很可能需要将其拆分为单独的方法。或者完全替换为 .NET 自己的验证机制。将冷任务与所有额外代码一起使用会使调试变得更加困难很多 - 这就是需要Unwrap 的原因。它也没有解决验证问题,它用另一层代码覆盖它——这也不能很好地工作。
  • 我不同意你所说的:“所有这些额外的代码”太夸张了,因为我在 Task.Run 之前将所有对验证方法的调用换成了一行代码,即展开。所以我实际上像这样考虑了我的代码,使任务创建更容易用更少的代码行来实现。所以是的,它解决了问题,不,它没有用代码层覆盖它。现在关于 Task.Run 的使用,我同意它应该尽可能地受到青睐。
猜你喜欢
  • 2014-07-19
  • 2016-06-18
  • 1970-01-01
  • 2014-03-13
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2021-12-04
  • 1970-01-01
相关资源
最近更新 更多