【发布时间】:2016-08-28 13:31:23
【问题描述】:
我想写一个接受多个参数的方法,包括一个动作和一个重试量并调用它。
所以我有这个代码:
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 是一个简单的任务>,它从数据库中获取一些东西,并且在本示例之外调用时可以正常工作。
每当调用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);
}
【问题讨论】:
-
您确定您的记录器是线程安全的吗?当我用 Console.WriteLine 替换它时,我得到“线程被中止”......还有什么是锁?你想做什么?
-
我真的对上面的例子感到困惑。为什么要尝试并行运行相同的操作 100 次? (
RunParallel方法)是对数据库进行某种负载测试吗? -
Logger 不是线程安全的,但对我来说不会崩溃。 @SergeSemenov 我正在使用 mongodb,我无法像在 SQL 中那样在一个过程中更新 100 个文件,所以我构建了一个方法来接受单个可枚举的操作列表并作为单个过程操作
-
你做错了,因为你运行你的
while (true)循环 100 次 -
我很乐意提供见解