如何监视和重新启动在C#中引发异常的任务?

如何监视和重新启动在C#中引发异常的任务?,c#,.net,asynchronous,async-await,C#,.net,Asynchronous,Async Await,假设我有一个程序,它实例化了三个无限期运行的任务。这些任务旨在并行运行。但是,假设这些任务由于网络错误而偶尔抛出异常 监视任务并在必要时重新启动它的最简单技术是什么 解决这个问题的方法是监视Task.Status数据,如果任务出现故障,只需调用Task.Start()方法 但是,此代码不起作用,因为任务的异常会导致整个应用程序崩溃 using System; using System.Collections.Generic; using System.Linq; using System.Tex

假设我有一个程序,它实例化了三个无限期运行的任务。这些任务旨在并行运行。但是,假设这些任务由于网络错误而偶尔抛出异常

监视任务并在必要时重新启动它的最简单技术是什么

解决这个问题的方法是监视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;

            }
        }

    }
}

如果线程出现异常,我的应用程序也会崩溃。 所以我学会了总是在任何线程或任务方法中添加try/catch。 此外,异常消息还有助于调试/改进程序

我只想:

  • 在任务方法中使用try-catch
  • 将异常用作任务的返回值
  • 使用WaitAny不要过于频繁地轮询,只在任务失败/结束时作出反应
  • 此处使用SortedDictionary来标识任务及其开始参数
代码如下:

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;
            }
        }

    }
}

由于无法重新启动
任务
,因此必须使用任务工厂
Func
,每当
任务
失败时,可以反复调用该工厂来创建更多任务

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);
    }
}
var taskFactories=new List();
taskFactorys.Add(()=>TaskMethod(“1”);
taskFactorys.Add(()=>TaskMethod(“2”));
taskFactorys.Add(()=>TaskMethod(“3”);
var runningTasks=taskFactories.ToDictionary(factory=>factory());
while(runningTasks.Count>0)
{
//等待事情发生,好的或坏的
var completedTask=wait Task.wheny(runningTasks.Keys);
if(completedTask.IsFaulted)//发生了错误
{
var factory=runningTasks[completedTask];
var newTask=factory();
runningTasks.Remove(completedTask);
runningTasks.Add(newTask,工厂);
}
else//任务刚刚正常完成或被取消
{
runningTasks.Remove(completedTask);
}
}

您无法“重新启动”任务。为什么不捕获任务中的异常,并让任务本身循环?顺便说一句,假设您使用的是C#7+,您可能希望使用
公共静态异步任务Main
Wait Program.MainAsync
而不是使用
.Wait()
。否则仙女就会失去翅膀;)非常好地使用了factory,这是高效重启机制的核心+1这似乎有效,但我不理解“var runningTasks=taskFactories.ToDictionary(factory=>factory())”。为什么需要将列表转换为字典?factory()方法定义在哪里?@Izzo
ToDictionary
在当前运行的任务和它们的工厂之间创建一个映射,以便在出现异常时可以找到正确的工厂,然后再次调用。工厂方法是存储在
taskFactories
列表中的lambda,也作为值存储在字典中。例如,
()=>TaskMethod(“1”)
是一个工厂。调用时,它会创建一个新任务。很好,不过请看-
if(0谢谢这个新术语。Yoda的讲话是一个品味问题。我从一本“代码完成”的书中得到了它,看起来Microsoft使用过一次。没问题。:)是的,完全可能。一切都很好,先生!
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);
    }
}