繁体   English   中英

Task.Factory.StartNew中的随机任务永远不会完成

[英]Random tasks from Task.Factory.StartNew never finishes

我正在使用Task.Factory方法进行异步等待。

public async Task<JobDto> ProcessJob(JobDto jobTask)
{
    try
    {
        var T = Task.Factory.StartNew(() =>
        {
            JobWorker jobWorker = new JobWorker();
            jobWorker.Execute(jobTask);
        });

        await T;
    }

我在这样的循环内调用此方法

for(int i=0; i < jobList.Count(); i++)
{
    tasks[i] = ProcessJob(jobList[i]);
}

我注意到的是,新任务在Process Explorer中打开,它们也开始工作(基于日志文件)。 然而,在十个有时八或七个完成中。 其他人再也不会回来了。

  1. 为什么会这样呢?
  2. 他们超时了吗? 在哪里可以为我的任务设置超时?

UPDATE

基本上在上面,我希望每个Task在被调用后立即开始运行,并等待AWAIT T关键字的响应。 我在这里假设一旦他们完成,每个人都会回到Await T并进行下一步行动。 我很高兴看到10个任务中有7个的结果,但是其中3个没有回来。

谢谢

如果没有其余的代码,很难说出问题所在,但是可以通过使ProcessJob同步,然后调用Task.Run来简化代码。

public JobDto ProcessJob(JobDto jobTask)
{
    JobWorker jobWorker = new JobWorker();
    return jobWorker.Execute(jobTask);
}

启动任务,等待所有任务完成。 最好使用Task.Run而不是Task.Factory.StartNew因为它为将工作推送到后台提供了更有利的默认设置。 这里

for(int i=0; i < jobList.Count(); i++)
{
    tasks[i] = Task.Run(() => ProcessJob(jobList[i]));
}

try
{
   await Task.WhenAll(tasks);
}
catch(Exception ex)
{
   // handle exception
}

您的代码可能吞没了异常。 我将在开始新任务的代码部分的末尾添加一个ContineWith调用。 像这样未经测试的代码:

var T = Task.Factory.StartNew(() =>
        {
            JobWorker jobWorker = new JobWorker();
            jobWorker.Execute(jobTask);
        }).ContinueWith(tsk =>
        {
            var flattenedException = tsk.Exception.Flatten();
            Console.Log("Exception! " + flattenedException);
            return true;
         });
        },TaskContinuationOptions.OnlyOnFaulted);  //Only call if task is faulted

另一种可能性是其中一项任务发生超时(如您所述)或死锁。 要跟踪超时(或死锁)是否是根本原因,可以添加一些超时逻辑(如 SO答案中所述):

int timeout = 1000; //set to something much greater than the time it should take your task to complete (at least for testing)
var task = TheMethodWhichWrapsYourAsyncLogic(cancellationToken);
if (await Task.WhenAny(task, Task.Delay(timeout, cancellationToken)) == task)
{
    // Task completed within timeout.
    // Consider that the task may have faulted or been canceled.
    // We re-await the task so that any exceptions/cancellation is rethrown.
    await task;

}
else
{
    // timeout/cancellation logic
}

在MSDN上查看有关TPL中异常处理的文档

首先,让我们制作代码的可复制版本。 这不是实现您正在做的事情的最佳方法,而是向您展示代码中正在发生的事情!

我将使代码与您的代码几乎相同,除了我将使用简单的int而不是您的JobDto并且在作业Execute()完成时,我将写入一个文件中,以便稍后进行验证。 这是代码

public class SomeMainClass
{
    public void StartProcessing()
    {
        var jobList = Enumerable.Range(1, 10).ToArray();
        var tasks = new Task[10];
        //[1] start 10 jobs, one-by-one
        for (int i = 0; i < jobList.Count(); i++)
        {
            tasks[i] = ProcessJob(jobList[i]);
        }
        //[4] here we have 10 awaitable Task in tasks
        //[5] do all other unrelated operations
        Thread.Sleep(1500); //assume it works for 1.5 sec
        // Task.WaitAll(tasks); //[6] wait for tasks to complete
        // The PROCESS IS COMPLETE here
    }

    public async Task ProcessJob(int jobTask)
    {
        try
        {
            //[2] start job in a ThreadPool, Background thread
            var T = Task.Factory.StartNew(() =>
            {
                JobWorker jobWorker = new JobWorker();
                jobWorker.Execute(jobTask);
            });
            //[3] await here will keep context of calling thread
            await T; //... and release the calling thread
        }
        catch (Exception) { /*handle*/ }
    }
}

public class JobWorker
{
    static object locker = new object();
    const string _file = @"C:\YourDirectory\out.txt";
    public void Execute(int jobTask) //on complete, writes in file
    {
        Thread.Sleep(500); //let's assume does something for 0.5 sec
        lock(locker)
        {
            File.AppendAllText(_file, 
                Environment.NewLine + "Writing the value-" + jobTask);
        }
    }
}

仅运行StartProcessing() ,这就是我在文件中得到的

Writing the value-4
Writing the value-2
Writing the value-3
Writing the value-1
Writing the value-6
Writing the value-7
Writing the value-8
Writing the value-5

至此,完成了8/10个工作。 显然,每次运行此命令时,编号和顺序可能都会更改。 但是,关键是,所有工作都没有完成!

现在,如果我取消注释步骤[6] Task.WaitAll(tasks); ,这就是我在文件中得到的

Writing the value-2
Writing the value-3
Writing the value-4
Writing the value-1
Writing the value-5
Writing the value-7
Writing the value-8
Writing the value-6
Writing the value-9
Writing the value-10

所以,我所有的工作都在这里完成!

代码注释中已经解释了为什么代码具有这种行为。 需要注意的主要事情是,您的任务在基于ThreadPoolBackground线程中运行。 因此,如果您不等待它们,则它们将在MAIN进程结束且主线程退出时被杀死!!

如果您仍然不想在那里等待任务,则可以从第一个方法返回任务列表,并在流程结束时await任务,就像这样

public Task[] StartProcessing()
{
    ...
    for (int i = 0; i < jobList.Count(); i++)
    {
        tasks[i] = ProcessJob(jobList[i]);
    }
    ...
    return tasks;
}

//in the MAIN METHOD of your application/process
var tasks = new SomeMainClass().StartProcessing();
// do all other stuffs here, and just at the end of process
Task.WaitAll(tasks);

希望这能消除所有的困惑。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM