我的代码中有类似的内容:
Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
Process(item);
});
问题是我在Process()
方法中做了很多事情(连接到文件共享,解析文件,保存到数据库等),我担心在这个过程中可能会出错迭代永远不会完成...... 这可能发生吗?
有没有办法为Process()
方法设置超时,以避免最终拥有僵尸线程?
更新
我发现设置超时的最简单方法是在CancellationTokenSource
上添加毫秒或在任务上调用Wait()
方法。
选项#1
Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
var cts = new CancellationTokenSource(2000);
Task task = Task.Factory.StartNew(() => Process(item), cts.Token);
});
选项#2
Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
Task task = Task.Factory.StartNew(() => Process(item));
task.Wait(2000);
});
问题是这些选项都无法取消Process()
方法。我是否需要在Process()
方法中检查某些内容?
答案 0 :(得分:2)
考虑将CancellationToken
添加到您的代码中。这样,您可以在任何时候正确取消所有操作。
然后,您可以使用CancelAfter()
方法。
答案 1 :(得分:2)
我最终结合了两个选项。它有效,但我不知道这是否是正确的方法。
解决方案:
Parallel.ForEach(myList, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var token = tokenSource.Token;
Task task = Task.Factory.StartNew(() => Process(item, token), token);
task.Wait();
});
并在Process()
我多次检查取消:
private void Process(MyItem item, CancellationToken token)
{
try
{
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
...sentences
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
...more sentences
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
...etc
}
catch(Exception ex)
Console.WriteLine("Operation cancelled");
答案 2 :(得分:1)
我的结果略有不同。就我而言,检查CancellationToken
在Process()
内,由于检查之间可能存在长时间运行的语句,因此无效。例如,如果我的超时是5秒,并且单个语句花了100秒...我不会知道,直到该语句完成,然后被if (token.IsCancellationRequested)
检测到。
这就是我最终做的事情
Parallel.ForEach(myList, (item) =>
{
Task task = Task.Factory.StartNew(() =>
{
Process(item));
});
if(task.Wait(10000)) // Specify overall timeout for Process() here
Console.WriteLine("Didn't Time out. All's good!");
else
Console.WriteLine("Timed out. Leave this one and continue with the rest");
});
然后在Process()
方法中,我添加了对可能长时间运行的语句的进一步检查,以允许它优雅地处理超时(尽可能多)。因此,只有在最坏的情况下,Process()必须由Task.Wait()
过早地停止。
private void Process(MyItem item)
{
...
cmd.CommandTimeout = 5; // The total of timeouts within Process() were
// set to be less than the total Task.Wait duration.
// Unfortunately some potentially long running methods
// don't have a built in timeout.
...
}