如果任务失败,重新启动任务

6
我正在尝试重新启动多个任务中的一个,如果它失败了。我使用.ContinueWith(t => { HandleException(t); }, TaskContinuationOptions.OnlyOnFaulted);,其中HandleException(t)方法应在现有任务数组中找到该任务并创建一个新任务来代替它。但是,传递给我的HandleException(t)方法的任务与原始任务不同,因此我无法在现有任务中找到它。此外,在处理异常时,现有任务仍在运行。
例如:
using System;
using System.Threading.Tasks;
using System.Threading;

static Task[] tasks;
static void Main(string[] args)
{
    tasks = new Task[2];
    for (int i = 0; i < tasks.Count(); i++)
    {
        tasks[i] = Task.Run(() => { Thread.Sleep(1000); throw new Exception("BOOM"); })
             .ContinueWith(t => { HandleException(t); }
             , TaskContinuationOptions.OnlyOnFaulted);

        Console.WriteLine(String.Format("Task {0} started", tasks[i].Id));
    }

    Console.ReadLine();
}

static void HandleException(Task task)
{
    Console.WriteLine(String.Format("Task {0} stopped", task.Id));

    // Check task status
    for (int i = 0; i < tasks.Count(); i++)
    {
        Console.WriteLine(String.Format("Task {0} status = {1}", i, tasks[i].Status));
    }
    // fFind and restart the task here
    if (tasks.Contains(task))
    {
        int index = Array.IndexOf(tasks, task);
        tasks[index] = Task.Run(() => { Thread.Sleep(1000); throw new Exception("BOOM"); })
         .ContinueWith(t => { HandleException(t); }
         , TaskContinuationOptions.OnlyOnFaulted);

        Console.WriteLine(String.Format("Task {0} started", tasks[index].Id));
    }
}

我的应用程序会导致以下结果:

Task 3 started
Task 6 started
Task 5 stopped
Task 0 status = Running
Task 2 stopped
Task 1 status = Running
Task 0 status = Running
Task 1 status = Running

由于传递给HandleException(t)的任务与原始任务不同,因此它在tasks[]中未找到,因此未重新启动。同时查看tasks[]的状态,它尚未停止。该如何正确地重新启动我的任务?

1个回答

4

这是因为您将继续任务存储在数组中,而不是原始任务(也就是说,您存储了ContinueWith的结果)。要解决此问题,请存储原始任务:

var task = Task.Run(() => { Thread.Sleep(1000); throw new Exception("BOOM");});
task.ContinueWith(t => { HandleException(t); }
         , TaskContinuationOptions.OnlyOnFaulted);
tasks[i] = task;

太棒了,这确实是问题所在。它还修复了任务状态。 - MrEighteen
@Evk 由于HandleException(t)是一个递归调用,它最终会引入堆栈溢出问题吗? - Maxim Podkolzin
2
@MaximPodkolzin 不行,因为所有操作都在不同的线程上执行。新任务是通过 Task.Run 启动的,这会从线程池中获取一个线程 > 抛出异常 > 调用 HandleException,可能又在不同的线程上 > 再次调用 Task.Run 并从线程池中获取另一个线程(不能使用相同的线程,因为它正在忙于执行 HandleException)> 当前线程完成执行并返回到池中。对于堆栈溢出,应该使用相同的线程递归执行函数,但这里我们有不同的线程(因此有不同的堆栈)。 - Evk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接