[英]How would I monitor and restart tasks that throw exceptions in C#?
假设我有一个程序可以实例化三个无限期运行的任务。 这些任务旨在并行运行。 但是,假设已知这些任务偶尔会因网络错误而抛出异常。
监视任务并在必要时重新启动它的最简单技术是什么?
我解决这个问题的方法是监视 Task.Status 数据,如果任务出错,只需调用 Task.Start() 方法。
但是,此代码不起作用,因为任务异常会导致整个应用程序崩溃。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var my_task = Program.MainAsync();
my_task.Wait();
}
public static async Task MainAsync()
{
var task_1 = Program.TaskMethod("1");
var task_2 = Program.TaskMethod("2");
var task_3 = Program.TaskMethod("3");
// loop indefinitely restarting task if necessary
while(true)
{
if (task_1.Status == TaskStatus.Faulted)
task_1.Start();
if (task_2.Status == TaskStatus.Faulted)
task_2.Start();
if (task_3.Status == TaskStatus.Faulted)
task_3.Start();
await Task.Delay(1000);
}
}
public static async Task TaskMethod(string task_id)
{
Console.WriteLine("Starting Task {0}", task_id);
while(true)
{
await Task.Delay(5000);
Console.WriteLine("Hello from task {0}", task_id);
int i = 0;
int b = 32 / i;
}
}
}
}
由于Task
无法重新启动,因此您必须使用任务工厂Func<Task>
每次Task
失败时都可以反复调用,以创建更多任务。
var taskFactories = new List<Func<Task>>();
taskFactories.Add(() => TaskMethod("1"));
taskFactories.Add(() => TaskMethod("2"));
taskFactories.Add(() => TaskMethod("3"));
var runningTasks = taskFactories.ToDictionary(factory => factory());
while (runningTasks.Count > 0)
{
// Wait for something to happen, good or bad
var completedTask = await Task.WhenAny(runningTasks.Keys);
if (completedTask.IsFaulted) // Something bad happened
{
var factory = runningTasks[completedTask];
var newTask = factory();
runningTasks.Remove(completedTask);
runningTasks.Add(newTask, factory);
}
else // A task just finished normally or was canceled
{
runningTasks.Remove(completedTask);
}
}
如果线程出现异常,我也会崩溃。 所以我学会了总是将 try/catch 添加到任何线程或任务方法。 此外,异常消息可以帮助调试/改进程序。
我只想:
这是代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var my_task = Program.MainAsync();
my_task.Wait();
}
public static async Task MainAsync()
{
var tasks = new SortedDictionary<string, Task<Exception>>();
for (int idx = 1; idx <= 3; idx++)
{
var parameter = idx.ToString();
tasks.Add(parameter, Program.TaskMethod(parameter));
}
// loop indefinitely restarting task if necessary
while (tasks.Any())
{
var taskArr = tasks.Values.ToArray();
var finishedIdx = Task.WaitAny(taskArr, 30000);
if (0 <= finishedIdx)
{
var finishedTask = taskArr[finishedIdx];
var parameter = tasks.First(kvp => kvp.Value == finishedTask).Key;
if (finishedTask.Result != null) // exception was thrown
{
tasks[parameter] = Program.TaskMethod(parameter); // restart the task!
}
else
{
tasks.Remove(parameter);
}
}
}
}
public static async Task<Exception> TaskMethod(string task_id)
{
try
{
Console.WriteLine("Starting Task {0}", task_id);
while (true)
{
await Task.Delay(5000);
Console.WriteLine("Hello from task {0}", task_id);
int i = 0;
int b = 32 / i;
}
return null;
}
catch (Exception exc)
{
return exc;
}
}
}
}
然后输出是这样的:
Starting Task 1
Starting Task 2
Starting Task 3
Hello from task 3
Hello from task 1
Hello from task 2
Starting Task 2
Starting Task 1
Starting Task 3
Hello from task 2
Hello from task 1
Hello from task 3
Starting Task 2
Starting Task 3
Starting Task 1
Hello from task 2
Starting Task 2
Hello from task 3
Hello from task 1
Starting Task 3
Starting Task 1
Hello from task 2
Hello from task 1
Starting Task 2
Hello from task 3
...
我正在处理失败的“重新启动”任务的类似问题,我尝试了Theodor Zoulias的回答,但它对我不起作用,因为使用任务本身作为字典键在我的情况下引发了异常(键已经存在)。 我确实使用答案作为我自己解决方案的起点。 这是类似于我想出的东西,它使用字符串将工厂和任务链接在一起:
var taskFactories = new Dictionary<string, Func<Task>>();
taskFactories.Add("task1", () => TaskMethod("task1"));
taskFactories.Add("task2", () => TaskMethod("task2"));
taskFactories.Add("task3", () => TaskMethod("task3"));
//Starts the tasks and stores them in a new dictionary.
var runningTasks = taskFactories.ToDictionary(x => x.Key, x => x.Value());
while (runningTasks.Count > 0)
{
//Wait for something to happen, good or bad
var completedTask = await Task.WhenAny(runningTasks.Values);
string taskId = runningTasks
.Where(x => x.Value == completedTask)
.Select(x => x.Key)
.First();
if (completedTask.IsFaulted) //Something bad happened
{
Func<Task> factory = taskFactories[taskId];
Task newTask = factory();
runningTasks.Remove(taskId);
runningTasks.Add(taskId, newTask);
}
else //The task finished normally or was canceled
{
runningTasks.Remove(taskId);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.