我在竞争状态下运行而难以重现,而且从我的代码分析来看,它似乎来自一个不执行的延续(或者直到结束)。
这是一些呈现上下文的伪代码:
Task<Something> GetObject(string id)
{
// some async code retrieving the object
}
List<Task> tasks = new List<Task>();
List<Something> result = new List<Something>();
foreach (var id in someList)
{
tasks.Add(GetObject(id).ContinueWith(task =>
{
var something = task.Result;
// do some basic sync stuff on something
result.Add(something);
}));
}
await Task.WhenAll(tasks);
随机发生的结果是缺少一些预期的对象。
出于某种原因,我知道foreach
进展顺利,并且没有理由不将所有必需的任务添加到List<Task>
。
仪表或调试尚未给出结果,因为它似乎改善了竞争条件......我希望有人可以在这里发光。谢谢!
答案 0 :(得分:3)
出于某种原因,我知道foreach顺利而且没有 为什么所有必需的任务都不会被添加到 列表。
好吧,也许List不是线程安全的。
如果列表'someList'很短或很长或者可能有重复值,我不知道您的问题,否则请考虑锁定添加和/或an Immutable list。
下一个链接有一个很好的解决方案: List<T> thread safety
答案 1 :(得分:2)
列表不是线程安全的。因此,如果2个线程(或更多!)想要向其添加元素,则一切都会发生(根据文档)。实际上,你可能会有一些缺失的附加元素。
如果您想继续使用List,您必须自己进行同步(可能使用锁或更多高级机制)。
您还可以使用其他适合您需要的收藏类型。 System.Collections.Concurrent namespace中的类型保证是线程安全的,但当然比普通的旧List更贵一点。
最后,您的示例似乎根本不需要任何集合操作:Task.WhenAll()返回Task<TResult[]>
。您可以这样重写代码:
Something[] result;
result = await Task.WhenAll(somelist.Select(id => GetObject(id));
这更短,更有效,希望更容易阅读。