C# 重试上一个任务操作TPL
我想实现一个重试任务,该任务执行上一个失败的任务操作并重复该操作 这就是我目前所拥有的。但是,它只是重复任务出错的事实,而不是再次实际启动任务的操作C# 重试上一个任务操作TPL,c#,task-parallel-library,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) {
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;
}
理想情况下,我希望我的实现如下所示:
try
{
await MakeFailure().Retry(5, 1000);
}
catch (Exception ex)
{
Console.WriteLine("I had an exception");
}
这可能是不可能的,但在将代码重构为RetryFunc任务之前,我想确保有一个非常棒的库,您可以利用它而无需编写自己的代码。它被称为瞬态故障应用程序块。但我首先要评估一个名为 它的使用方式与上面的代码非常相似。下面是一个简单的例子:
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");
}
}
}
有一个非常棒的库,您可以不用编写自己的代码就可以利用它。它被称为瞬态故障应用程序块。但我首先要评估一个名为 它的使用方式与上面的代码非常相似。下面是一个简单的例子:
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");
}
}
}
你的问题是,一旦你有一个任务在飞行,它不能撤消或重试。您必须从Func开始才能重试 现在您可以直接使用TPL,但我建议您使用Microsoft的反应式框架来构建所需的功能。它比第三方物流强大得多 给定Func,这就是您需要的:
Func<Task<T>> taskFactory = ...
int retryCount = 5;
Task<T> retryingTask = Observable.FromAsync(taskFactory).Retry(retryCount).ToTask();
我用以下代码测试了这一点:
var i = 0;
Func<Task<int>> taskFactory = () => Task.Run(() =>
{
if (i++ == 0)
throw new Exception("Foo");
return i;
});
int retryCount = 5;
Task<int> retryingTask = Observable.FromAsync(taskFactory).Retry(retryCount).ToTask();
Console.WriteLine(retryingTask.Result);
反应式框架可以让您做更多的事情——它是一个非常强大的库——但它确实使这项任务变得非常简单。您可以使用NuGet Rx Main来获取位。您遇到的问题是,一旦任务处于运行状态,它就无法撤消或重试。您必须从Func开始才能重试 现在您可以直接使用TPL,但我建议您使用Microsoft的反应式框架来构建所需的功能。它比第三方物流强大得多 给定Func,这就是您需要的:
Func<Task<T>> taskFactory = ...
int retryCount = 5;
Task<T> retryingTask = Observable.FromAsync(taskFactory).Retry(retryCount).ToTask();
我用以下代码测试了这一点:
var i = 0;
Func<Task<int>> taskFactory = () => Task.Run(() =>
{
if (i++ == 0)
throw new Exception("Foo");
return i;
});
int retryCount = 5;
Task<int> retryingTask = Observable.FromAsync(taskFactory).Retry(retryCount).ToTask();
Console.WriteLine(retryingTask.Result);
反应式框架可以让您做更多的事情——它是一个非常强大的库——但它确实使这项任务变得非常简单。您可以使用NuGet Rx Main来获取位
不完全反对。但它将代码流更改为我不喜欢的错误优先布局
考虑你的类型。任务表示异步操作。在异步世界中,任务表示已启动的异步操作。任务不是您可以重试的
另一方面,Func表示可以启动的异步操作。或者重新启动。这就是你需要解决的问题
使用适当的类型后,代码就简单明了了:
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);
}
像另一个回答者一样,我建议你使用一个真正为此而设计的库。和是两个很好的例子
不完全反对。但它将代码流更改为我不喜欢的错误优先布局
考虑你的类型。任务表示异步操作。在异步世界中,任务表示已启动的异步操作。任务不是您可以重试的
另一方面,Func表示可以启动的异步操作。或者重新启动。这就是你需要解决的问题
使用适当的类型后,代码就简单明了了:
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);
}
像另一个回答者一样,我建议你使用一个真正为此而设计的库。和是两个很好的例子。您是否反对不将其作为扩展方法,并将方法签名更改为静态异步任务RetryFunc taskFactory、int retryCount等?这不是完全反对的。但它将代码流更改为错误优先的布局,我不喜欢Retryasync=>await MakeFailure,51000没有await MakeFailure干净。Retry51000不要试图攻击TPL来做它不适合做的事情。在上面创建一个抽象。你会反对不将其作为扩展方法,并将方法签名更改为static async Task RetryFunc taskFactory,int retryCount,…?这不是完全反对的。但它将代码流更改为错误优先的布局,我不喜欢Retryasync=>await MakeFailure,51000没有await MakeFailure干净。Retry51000不要试图攻击TPL来做它不适合做的事情。在其上创建一个抽象概念,而概念是合理的。我发现对于这个框架来说,API的冗长本质非常健谈。一个好的答案,尽管这个概念是正确的。我发现对于这个框架来说,API的冗长本质非常健谈。很好的答案尽管如此,在使用第三方物流时,我同意这是最好的答案。我希望一个任务能够保留一些关于它正在处理的操作的细节,但它似乎没有。@JonathanSheely:在现代异步/等待代码中,大多数任务甚至没有操作。在使用TPL时,我同意这是最好的
回答。我希望某个任务能够保留它正在处理的操作的一些细节,但它似乎没有。@JonathanSheely:在现代异步/等待代码中,大多数任务甚至没有操作。我已经在许多其他项目中使用Rx。这正是为什么我想知道是否有一种方法可以在TPL中只为一个重试例程进行一次简短的实现。@JonathanSheely-这很公平。比起第三方物流,我更喜欢Rx。我已经在我的许多其他项目中改用Rx。这正是为什么我想知道是否有一种方法可以在TPL中只为一个重试例程进行一次简短的实现。@JonathanSheely-这很公平。我更喜欢Rx而不是TPL。