C# - 等待数组中的所有任务完成或失败
C# - Wait For All Tasks In Array To Complete Or Fail
我有一个任务列表,这些任务在不同的数据库上工作,然后在中央数据库上更新一组结果。
如果我无法连接到中央数据库,所需的行为是取消所有任务,等待 运行ning 任务成功停止(因为它是合作的事情)然后退出程序。
我写了几行代码来测试取消任务:
var cancellationTokenSource = new CancellationTokenSource();
var taskList = new List<Task>();
taskList.Add(new Task(() => { Thread.Sleep(5000); }, cancellationTokenSource.Token));
taskList.Add(new Task(() => { Thread.Sleep(5000); }, cancellationTokenSource.Token));
taskList.ForEach(task => task.Start());
cancellationTokenSource.Cancel();
Task.WaitAll(taskList.ToArray());
我知道我通常应该在执行代码的任务中定期检查取消令牌,但现在我只是测试我对取消工作原理的理解。
当我 运行 它时,我得到错误:
您可以将取消令牌传递给 WaitAll,但那是为了实际取消 WaitAll
基本上我只想等到所有任务都 运行 完成或因被取消而停止,这样我就可以安全地退出。
我觉得我可能遗漏了一些简单的东西,如果有人能阐明如何实现这一点,我将不胜感激。
非常感谢
这可能与
重复
基本上你需要的是
Task.WhenAll(taskA, taskB, taskC).Wait()
如上面的回答所述。
首先,避免将阻塞代码(Task.WaitAll
、Wait
、...)与非阻塞代码混合; Task.WhenAll
可能是更好的选择。正如 Amogh 的回答 中的 post 所暗示的那样,您可能想改用它:
await Task.WhenAll(taskList.ToArray())
.ContinueWith(t => { }, TaskContinuationOptions.NotOnRanToCompletion);
例如,下面的代码将打印Canceled
、RanToCompletion
和Faulted
:
var cancellationTokenSource = new CancellationTokenSource();
var taskList = new List<Task>
{
new Task(() => { Thread.Sleep(1000); }, cancellationTokenSource.Token),
new Task(() => { Thread.Sleep(5000); }),
new Task(() =>
{
Thread.Sleep(3000);
throw new InvalidOperationException();
})
};
taskList.ForEach(task => task.Start());
cancellationTokenSource.Cancel();
await Task.WhenAll(taskList.ToArray())
.ContinueWith(t => { }, TaskContinuationOptions.NotOnRanToCompletion);
Console.WriteLine(taskList[0].Status);
Console.WriteLine(taskList[1].Status);
Console.WriteLine(taskList[2].Status);
也就是说,这只有在您打算之后观察各个任务时才有意义。如果您不这样做,请接受例外情况。
TaskCanceledException
是一个异常,在您取消 运行 任务时总是抛出该异常。您只需要使用 try-catch 块来处理它。这违背了微软自己的哲学,即尽可能避免抛出异常 (Best Practices For Exceptions).
最简单的解决方案如下所示:
var cancellationTokenSource = new CancellationTokenSource();
var taskList = new List<Task>
{
Task.Run(() => { Thread.Sleep(1); }),
Task.Run(() => { Thread.Sleep(5000); }, cancellationTokenSource.Token),
Task.Run(() => { Thread.Sleep(5000); }, cancellationTokenSource.Token)
};
cancellationTokenSource.Cancel();
try
{
Task.WhenAll(taskList).GetAwaiter().GetResult();
}
catch (TaskCanceledException)
{
Console.WriteLine("Tasks were cancelled");;
}
Console.WriteLine(taskList[0].Status);
Console.WriteLine(taskList[1].Status);
Console.WriteLine(taskList[2].Status);
我有一个任务列表,这些任务在不同的数据库上工作,然后在中央数据库上更新一组结果。
如果我无法连接到中央数据库,所需的行为是取消所有任务,等待 运行ning 任务成功停止(因为它是合作的事情)然后退出程序。
我写了几行代码来测试取消任务:
var cancellationTokenSource = new CancellationTokenSource();
var taskList = new List<Task>();
taskList.Add(new Task(() => { Thread.Sleep(5000); }, cancellationTokenSource.Token));
taskList.Add(new Task(() => { Thread.Sleep(5000); }, cancellationTokenSource.Token));
taskList.ForEach(task => task.Start());
cancellationTokenSource.Cancel();
Task.WaitAll(taskList.ToArray());
我知道我通常应该在执行代码的任务中定期检查取消令牌,但现在我只是测试我对取消工作原理的理解。
当我 运行 它时,我得到错误:
您可以将取消令牌传递给 WaitAll,但那是为了实际取消 WaitAll
基本上我只想等到所有任务都 运行 完成或因被取消而停止,这样我就可以安全地退出。
我觉得我可能遗漏了一些简单的东西,如果有人能阐明如何实现这一点,我将不胜感激。
非常感谢
这可能与
基本上你需要的是
Task.WhenAll(taskA, taskB, taskC).Wait()
如上面的回答所述。
首先,避免将阻塞代码(Task.WaitAll
、Wait
、...)与非阻塞代码混合; Task.WhenAll
可能是更好的选择。正如 Amogh 的回答
await Task.WhenAll(taskList.ToArray())
.ContinueWith(t => { }, TaskContinuationOptions.NotOnRanToCompletion);
例如,下面的代码将打印Canceled
、RanToCompletion
和Faulted
:
var cancellationTokenSource = new CancellationTokenSource();
var taskList = new List<Task>
{
new Task(() => { Thread.Sleep(1000); }, cancellationTokenSource.Token),
new Task(() => { Thread.Sleep(5000); }),
new Task(() =>
{
Thread.Sleep(3000);
throw new InvalidOperationException();
})
};
taskList.ForEach(task => task.Start());
cancellationTokenSource.Cancel();
await Task.WhenAll(taskList.ToArray())
.ContinueWith(t => { }, TaskContinuationOptions.NotOnRanToCompletion);
Console.WriteLine(taskList[0].Status);
Console.WriteLine(taskList[1].Status);
Console.WriteLine(taskList[2].Status);
也就是说,这只有在您打算之后观察各个任务时才有意义。如果您不这样做,请接受例外情况。
TaskCanceledException
是一个异常,在您取消 运行 任务时总是抛出该异常。您只需要使用 try-catch 块来处理它。这违背了微软自己的哲学,即尽可能避免抛出异常 (Best Practices For Exceptions).
最简单的解决方案如下所示:
var cancellationTokenSource = new CancellationTokenSource();
var taskList = new List<Task>
{
Task.Run(() => { Thread.Sleep(1); }),
Task.Run(() => { Thread.Sleep(5000); }, cancellationTokenSource.Token),
Task.Run(() => { Thread.Sleep(5000); }, cancellationTokenSource.Token)
};
cancellationTokenSource.Cancel();
try
{
Task.WhenAll(taskList).GetAwaiter().GetResult();
}
catch (TaskCanceledException)
{
Console.WriteLine("Tasks were cancelled");;
}
Console.WriteLine(taskList[0].Status);
Console.WriteLine(taskList[1].Status);
Console.WriteLine(taskList[2].Status);