Task.Factory.StartNew中的随机任务永远不会完成
本文关键字:永远 任务 随机 Factory StartNew Task | 更新日期: 2023-09-27 18:15:43
我正在使用Async await与任务。工厂方法。
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]);
}
我注意到的是新的任务在进程资源管理器中打开,它们也开始工作(基于日志文件)。然而,10个人中有时有8个或7个完成。剩下的人就再也不回来了。
- 为什么会发生这种情况?他们超时了吗?我可以在哪里为我的任务设置超时?
基本上在上面,我希望每个任务在被调用后立即开始运行,并等待AWAIT T关键字的响应。我在这里假设,一旦它们完成任务,它们中的每一个都将回到Await T并执行下一个动作。我已经在7/10的任务中看到了这个结果,但其中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
}
首先,让我们创建一个可复制的代码版本。这不是实现你所做的事情的最佳方式,但它可以向你展示代码中正在发生的事情!
我将保持代码几乎与您的代码相同,除了我将使用简单的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
所以,我在这里完成了所有的工作!
为什么代码是这样的行为,已经在代码注释中解释了。需要注意的主要事情是,您的任务在基于Background
线程的ThreadPool
中运行。所以,如果你不等待它们,它们将在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);
有可能你的代码吞下了异常。我会在开始新任务的代码部分的末尾添加一个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中异常处理的文档