重试上一个任务操作TPL

时间:2015-08-25 21:49:52

标签: c# task-parallel-library

我想实现一个重试任务,该任务执行先前的失败任务操作并重复它。

这是我到目前为止所拥有的。然而,它只是重复这样一个事实,即任务是错误的,而不是实际再次触发任务的动作。

public static async Task<T> Retry<T>(this Task<T> task, int retryCount, int delay, TaskCompletionSource<T> tcs = null)
{
    if (tcs == null)
    {
        tcs = new TaskCompletionSource<T>();
    }

    await task.ContinueWith(async _original =>
    {
        if (_original.IsFaulted)
        {
            if (retryCount == 0)
            {
                tcs.SetException(_original.Exception.InnerExceptions);
            }
            else
            {
                Console.WriteLine("Unhandled exception. Retrying...");

                await Task.Delay(delay).ContinueWith(async t =>
                {
                    await Retry(task, retryCount - 1, delay, tcs);
                });
            }
        }
        else
            tcs.SetResult(_original.Result);
    });
    return await tcs.Task;
}

我试图通过一点反思来获得动作。但是,似乎任务完成后,操作将设置为null。

var action = task
    .GetType()
    .GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance)
    .GetValue(task) as Action;

理想情况下,我希望我的实现看起来像这样:

try
{
    await MakeFailure().Retry(5, 1000);
}
catch (Exception ex)
{
    Console.WriteLine("I had an exception");
}

这可能无法实现,但我想在将代码重构为Retry(Func<T> task)之前确定

2 个答案:

答案 0 :(得分:2)

  

并非完全反对它。但它将代码流改变为我不喜欢的故障第一布局

考虑你的类型。 Task表示异步操作。在异步世界中,Task表示已启动的异步操作 Task不是你可以“重试”的东西。

另一方面,Func<Task>表示可以启动的异步操作。或重新启动。这就是你需要使用的东西。

使用适当的类型后,代码很简单:

public static async Task<T> Retry<T>(Func<Task<T>> action, int retryCount, int delay)
{
  while (retryCount > 0)
  {
    try
    {
      return await action().ConfigureAwait(false);
    }
    catch (Exception ex)
    {
      await Task.Delay(delay).ConfigureAwait(false);
      --retryCount;
    }
  }
  return await action().ConfigureAwait(false);
}

与其他回答者一样,我建议您使用实际为此设计的库。 Transient Fault Handling Application BlockPolly是两个很好的例子。

答案 1 :(得分:1)

有一个非常棒的库,你可以在不编写自己的代码的情况下利用它。它被称为瞬态故障应用程序块。但我首先要评估名为TransientFaultHandling.Core的块中的单个库。

它的使用方式与上面的代码非常相似。这是一个简单的例子:

using System;
using Microsoft.Practices.TransientFaultHandling;

namespace Stackoverflow
{
    class Program
    {
        internal class MyTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
        {
            public bool IsTransient(Exception ex)
            {
                return true;
            }
        }

        private static void Main(string[] args)
        {
            const int retryCount = 5;
            const int retryIntervalInSeconds = 1;

            // define the strategy for retrying
            var retryStrategy = new FixedInterval(
                retryCount,
                TimeSpan.FromSeconds(retryIntervalInSeconds));

            // define the policy 
            var retryPolicy =
                new RetryPolicy<MyTransientErrorDetectionStrategy>(retryStrategy);

            retryPolicy.Retrying += retryPolicy_Retrying;

            for (int i = 0; i < 50; i++)
            {
                // try this a few times just to illustrate

                try
                {
                    retryPolicy.ExecuteAction(SomeMethodThatCanSometimesFail);

                    // (the retry policy has async support as well)
                }
                catch (Exception)
                {
                    // if it got to this point, your retries were exhausted
                    // the original exception is rethrown
                    throw;
                }
            }

            Console.WriteLine("Press Enter to Exit");

            Console.ReadLine();
        }

        private static void SomeMethodThatCanSometimesFail()
        {
            var random = new Random().Next(1, 4);

            if (random == 2)
            {
                const string msg = "randomFailure";

                Console.WriteLine(msg);

                throw new Exception(msg);
            }
        }

        private static void retryPolicy_Retrying(object sender, RetryingEventArgs e)
        {
            Console.WriteLine("retrying");
        }
    }
}