2

我有 System.Collections.Generic.SynchronizedCollection 共享集合。我们的代码使用 .Net 4.0 任务库来跨越线程并将同步集合传递给线程。到目前为止,线程还没有在集合中添加或删除项目。但是需要一个线程的新要求必须从集合中删除项目,而另一个线程只读取集合。在从集合中删除项目之前,我是否需要添加锁定?如果是这样,阅读器线程是否是线程安全的?或建议获得线程安全的最佳方法?

4

3 回答 3

8

不,它不是完全线程安全的。在一个简单的控制台应用程序中尝试以下操作,看看它是如何因异常而崩溃的:

var collection = new SynchronizedCollection<int>();

var n = 0;

Task.Run(
    () =>
        {
            while (true)
            {
                collection.Add(n++);
                Thread.Sleep(5);
            }
        });

Task.Run(
    () =>
        {
            while (true)
            {
                Console.WriteLine("Elements in collection: " + collection.Count);

                var x = 0;
                if (collection.Count % 100 == 0)
                {
                    foreach (var i in collection)
                    {
                        Console.WriteLine("They are: " + i);
                        x++;
                        if (x == 100)
                        {
                            break;
                        }

                    }
                }
            }
        });

Console.ReadKey();

在此处输入图像描述

请注意,如果您将 SynchronizedCollection 替换为 ConcurrentBag,您将获得线程安全:

var collection = new ConcurrentBag<int>();

SynchronizedCollection 在此应用程序中根本不是线程安全的。改用并发集合。

于 2016-03-24T18:12:31.803 回答
7

正如亚历山大已经指出的那样SynchronizedCollection,这种情况下不是线程安全的。实际上包装了一个普通的SynchronizedCollection通用列表,并且只是将每个调用委托给底层列表,并在调用周围加锁。这也是在GetEnumerator. 所以枚举器的获取是同步的,但不是实际的枚举。

var collection = new SynchronizedCollection<string>();
collection.Add("Test1");
collection.Add("Test2");
collection.Add("Test3");
collection.Add("Test4");

var enumerator = collection.GetEnumerator();
enumerator.MoveNext();
collection.Add("Test5");
//The next call will throw a InvalidOperationException ("Collection was modified")
enumerator.MoveNext();

使用 foreach 时,将以这种方式调用枚举器。因此,在枚举此数组之前添加 aToArray()也不起作用,因为这将首先枚举到此数组中。当您在 foreach 中执行操作时,此枚举可能会更快,因此它可以降低出现并发问题的可能性。正如理查德指出的那样:为了真正的线程安全,请选择System.Collections.Concurrent类。

于 2016-04-21T09:24:15.300 回答
2

是的,SynchronizedCollection 将为您进行锁定。

如果您有多个阅读器而只有一个编写器,您可能希望考虑使用 ReaderWriterLock,而不是 SynchronizedCollection。

此外,如果您是 .Net 4+,请查看System.Collections.Concurrent. 这些类的性能比 SynchronizedCollection 好得多。

于 2012-11-25T05:44:36.877 回答