为了账号安全,请及时绑定邮箱和手机立即绑定

同步集合 InvalidOperationException/System

同步集合 InvalidOperationException/System

C#
泛舟湖上清波郎朗 2022-12-24 14:45:44
我编写了一些类来使用SynchronizedCollection.class MultithreadTesting{    public readonly SynchronizedCollection<int> testlist = new SynchronizedCollection<int>();    public SynchronizedReadOnlyCollection<int> pubReadOnlyProperty    {        get        {            return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);        }    }    public void Test()    {        int numthreads = 20;        Thread[] threads = new Thread[numthreads];        List<Task> taskList = new List<Task>();        for (int i = 0; i < numthreads / 2; i++)        {            taskList.Add(Task.Factory.StartNew(() =>            {                for (int j = 0; j < 100000; j++)                {                    testlist.Add(42);                }            }));        }        for (int i = numthreads / 2; i < numthreads; i++)        {            taskList.Add(Task.Factory.StartNew(() =>            {                var sum = 0;                foreach (int num in pubReadOnlyProperty)                {                    sum += num;                }            }));        }        Task.WaitAll(taskList.ToArray());        testlist.Clear();    }}运行它我使用    MultithreadTesting test = new MultithreadTesting();    while (true)        test.Test();但是代码让我失望System.ArgumentException: 'Destination array was not long enough. Check destIndex and length, and the array's lower bounds.'如果我尝试使用testlistin foreach,我会得到System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'然而,MSDN 告诉我们SynchronizedReadOnlyCollection 类提供一个线程安全的只读集合,其中包含泛型参数指定类型的对象作为元素。
查看完整描述

1 回答

?
MYYA

TA贡献1868条经验 获得超4个赞

错误的根本原因是List<T>构造不是线程安全的。


让我们看看在构造 new 时会发生什么SynchronizedReadOnlyCollection。异常发生在以下行:


return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);

正如异常 StackTrace 告诉我们的那样,List<T>..ctor在构建过程中涉及到:


at System.Collections.Generic.SynchronizedCollection`1.CopyTo(T[] array, Int32 index)

at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)

at System.Collections.Generic.SynchronizedReadOnlyCollection`1..ctor(Object syncRoot, IEnumerable`1 list)

构造函数中的以下片段List<T>显示了错误发生的位置。代码是从MS 参考源中复制的,我清理了代码中不需要的部分以便于阅读。请注意,在注释 (1) 和 (2) 之间还有其他线程在操作集合:


public List(IEnumerable<T> collection) {

    ICollection<T> c = collection as ICollection<T>;

    // (1) count is now current Count of collection

    int count = c.Count;

    // other threads can modify collection meanwhile

    if (count == 0)

    {

        _items = _emptyArray;

    }

    else {

        _items = new T[count];

        // (2) SynchronizedCollection.CopyTo is called (which itself is thread-safe)

        // Collection can still be modified between (1) and (2) 

        // No when _items.Count != c.Count -> Exception is raised.

        c.CopyTo(_items, 0);

        _size = count;

    }

}

解决方案


testlist在构造 new 时,可以通过锁定修改轻松解决该问题SynchronizedReadOnlyCollection。


public SynchronizedReadOnlyCollection<int> pubReadOnlyProperty

{

    get

    {

        lock (testlist.SyncRoot)

        {

            return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);

        }

    }

}


查看完整回答
反对 回复 2022-12-24
  • 1 回答
  • 0 关注
  • 103 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信