我想编写一个接受多个参数的方法,包括一个动作和一个重试量,并调用它。
所以我有这段代码:
public static IEnumerable<Task> RunWithRetries<T>(List<T> source, int threads, Func<T, Task<bool>> action, int retries, string method)
{
object lockObj = new object();
int index = 0;
return new Action(async () =>
{
while (true)
{
T item;
lock (lockObj)
{
if (index < source.Count)
{
item = source[index];
index++;
}
else
break;
}
int retry = retries;
while (retry > 0)
{
try
{
bool res = await action(item);
if (res)
retry = -1;
else
//sleep if not success..
Thread.Sleep(200);
}
catch (Exception e)
{
LoggerAgent.LogException(e, method);
}
finally
{
retry--;
}
}
}
}).RunParallel(threads);
}
RunParallel是Action的扩展方法,它看起来像这样:
public static IEnumerable<Task> RunParallel(this Action action, int amount)
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < amount; i++)
{
Task task = Task.Factory.StartNew(action);
tasks.Add(task);
}
return tasks;
}
现在,问题是:线程正在消失或崩溃而不等待操作完成。
我写了这个示例代码:
private static async Task ex()
{
List<int> ints = new List<int>();
for (int i = 0; i < 1000; i++)
{
ints.Add(i);
}
var tasks = RetryComponent.RunWithRetries(ints, 100, async (num) =>
{
try
{
List<string> test = await fetchSmthFromDb();
Console.WriteLine("#" + num + " " + test[0]);
return test[0] == "test";
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
return false;
}
}, 5, "test");
await Task.WhenAll(tasks);
}
fetchSmthFromDb
是一个简单的任务&gt;从数据库中获取内容并在此示例之外调用时工作完全正常。
每当调用List<string> test = await fetchSmthFromDb();
行时,线程似乎正在关闭,Console.WriteLine("#" + num + " " + test[0]);
甚至没有被触发,同时调试断点永远不会被触发。
最终工作代码
private static async Task DoWithRetries(Func<Task> action, int retryCount, string method)
{
while (true)
{
try
{
await action();
break;
}
catch (Exception e)
{
LoggerAgent.LogException(e, method);
}
if (retryCount <= 0)
break;
retryCount--;
await Task.Delay(200);
};
}
public static async Task RunWithRetries<T>(List<T> source, int threads, Func<T, Task<bool>> action, int retries, string method)
{
Func<T, Task> newAction = async (item) =>
{
await DoWithRetries(async ()=>
{
await action(item);
}, retries, method);
};
await source.ParallelForEachAsync(newAction, threads);
}
答案 0 :(得分:6)
问题出在这一行:
TopMainFragment
您使用异步lambda启动异步操作,但不要返回任务以等待。即它运行在工作线程上,但您永远不会知道它何时完成。并且您的程序在异步操作完成之前终止 - 这就是您没有看到任何输出的原因。
需要:
return new Action(async () => ...
<强>更新强>
首先,您需要分配方法的职责,因此不要将重试策略(不应该硬编码到布尔结果的检查)与并行运行的任务混合。
然后,如前所述,您运行return new Func<Task>(async () => ...
循环100次而不是并行处理。
正如@MachineLearning指出的那样,使用while (true)
代替Task.Delay
。
总的来说,您的解决方案如下所示:
Thread.Sleep
您需要使用AsyncEnumerator NuGet Package才能使用using System.Collections.Async;
static async Task DoWithRetries(Func<Task> action, int retryCount, string method)
{
while (true)
{
try
{
await action();
break;
}
catch (Exception e)
{
LoggerAgent.LogException(e, method);
}
if (retryCount <= 0)
break;
retryCount--;
await Task.Delay(millisecondsDelay: 200);
};
}
static async Task Example()
{
List<int> ints = new List<int>();
for (int i = 0; i < 1000; i++)
ints.Add(i);
Func<int, Task> actionOnItem =
async item =>
{
await DoWithRetries(async () =>
{
List<string> test = await fetchSmthFromDb();
Console.WriteLine("#" + item + " " + test[0]);
if (test[0] != "test")
throw new InvalidOperationException("unexpected result"); // will be re-tried
},
retryCount: 5,
method: "test");
};
await ints.ParallelForEachAsync(actionOnItem, maxDegreeOfParalellism: 100);
}
命名空间中的ParallelForEachAsync
扩展名方法。
答案 1 :(得分:0)
除了最后的完整再造之外,我认为强调原始代码的真正错误非常重要。
0)首先,正如@Serge Semenov立即指出的那样,Action必须被替换为
Group_Y
但是还有其他两个必要的变化。
1)使用异步委托作为参数,必须使用更新的Task.Run而不是旧模式new TaskFactory.StartNew(否则你必须显式添加Unwrap())
2)此外ex()方法不能异步,因为Task.WhenAll必须等待Wait()并且没有等待。
此时,即使存在需要重新设计的逻辑错误,从纯粹的技术角度来看,它确实有效并且产生了输出。
可以在线获得测试:http://rextester.com/HMMI93124