为啥我在使用任务时无法正确填充集合? [复制]

Posted

技术标签:

【中文标题】为啥我在使用任务时无法正确填充集合? [复制]【英文标题】:Why collection doesn't fill properly when I am using tasks? [duplicate]为什么我在使用任务时无法正确填充集合? [复制] 【发布时间】:2021-06-02 20:06:47 【问题描述】:

我有一段非常简单的代码:

public class CustomTester

    public async Task RunTheTestAsync()
    
        IList<int> someCollectionToFill = new List<int>();

        var someCollectionToIterate = Enumerable.Range(0, 20);

        IEnumerable<Task> tasks = someCollectionToIterate.Select(
            valueFromCollection => Task.Run(async () =>
        
            try
            
                await Task.Delay(1);

                someCollectionToFill.Add(valueFromCollection);
            
            catch (Exception ex)
            
                Console.WriteLine(ex.Message);
            
        )).ToList();

        await Task.WhenAll(tasks);

        PrintCollectionValues(someCollectionToFill);
    

    private void PrintCollectionValues(IList<int> collection)
    
        Console.WriteLine("Total count: " + collection.Count);
    

我是这样执行的:

await new CustomTester().RunTheTestAsync();

当代码执行时,我有以下输出:

Total count: 13

或:

Total count: 14

等等……

但我期待:

Total count: 20

为什么会这样?

【问题讨论】:

您希望非线程安全集合在线程场景中安全工作的任何特殊原因? 这大约是您研究线程安全的时候,是的,锁在这里会有所帮助。但是,除非您了解它为什么会有所帮助,否则您是在盲目地飞行 您必须自己使用锁来创建“线程安全列表”,但幸运的是已经有一些并发集合。您可以将 ConcurrentBag 用于“someCollectionToFill”,一切都会好起来的。 一个列表基于一个非线程安全的数组,而这又只是一堆内存。每次添加到列表时,它都有可能调整基本数组的大小。由于您实际上无法调整数组的大小,因此运行时实际上需要创建一个更大的新数组,并复制当前内容。如果有 2 个线程同时尝试插入和复制,您可以想象事情会变得有趣。就像多人试图把东西放在一个桶里,桶装满了,然后你必须拿一个更大的桶把它倒进去。当一个人放东西,另一个人倾倒东西时会发生什么 @Phil 如果您在多个线程中使用非线程安全的东西,这并不意味着会自动引发异常。也有可能该对象未处于您所观察到的预期状态。 【参考方案1】:

你正在使用一个列表,一个非线程安全的集合,同时来自多个线程。这是不安全的。要么使用

lock(someCollectionToFill)
    someCollectionToFill.Add(valueFromCollection);

var someCollectionToFill = new ConcurrentBag<int>();

我强烈建议在尝试使用任何多线程之前阅读线程安全。 Multi threading best practices 是一个好的开始,但它并没有涵盖所有内容,所以我建议您在谷歌上搜索一下。编写多线程代码很困难,而且很容易出错,导致断断续续的 bug,很难调试和理解。

【讨论】:

谢谢。我一定会熟悉这个话题。

以上是关于为啥我在使用任务时无法正确填充集合? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个程序不允许我在需要时输入信息? [复制]

为啥我不能在编译时将整数添加到泛型集合中,即使使用引用类型作为数字创建的泛型? [复制]

为啥java中有两个接口可比较和比较器用于对集合进行排序? [复制]

带有数组的集合/表视图的详细视图?

独立填充集合视图的每个部分 Swift

从对象中的集合中删除对象时,.net core 2.1 razor 页面视图无法正确更新