使用. continuewith()的竞争条件

本文关键字:竞争 条件 continuewith 使用 | 更新日期: 2023-09-27 18:16:18

我在一个很难重现的竞争条件下运行,从我的代码分析来看,它似乎来自一个不执行的延续(或者直到它结束)。

下面是一些表示上下文的伪代码:
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>中。

检测或调试还没有给出结果,因为它似乎改善了竞争条件…我希望有人能给我点启示。谢谢!

使用. continuewith()的竞争条件

由于某种原因,我知道每一个都很顺利,没有为什么没有将所有必需的任务添加到列表。

也许List不是线程安全的。

我不知道从你的问题,如果列表' someelist '是短或长或可能有重复的值,否则考虑添加和/或不可变列表锁。

下一个链接有一个很好的解决方案:List线程安全

List不是线程安全的。因此,如果2个线程(或更多!)想要添加元素到它,一切都可以发生(根据文档)。实际上,您可能会丢失一些添加的元素。

如果你想继续使用List,你必须自己进行同步(可能使用锁或更高级的机制)。

您还可以使用其他适合您需要的集合类型。来自System.Collections.Concurrent命名空间的类型保证是线程安全的,但当然比普通的旧List要贵一点。

最后,看起来你的例子根本不需要任何集合操作:Task.WhenAll()返回一个Task<TResult[]>。你可以这样重写你的代码:

Something[] result;
result = await Task.WhenAll(somelist.Select(id => GetObject(id));

更短,更有效,希望更容易阅读。