• UWP多线程枚举安全的List


      最近在做windows runtime下APP开发的工作。在Service层请求返回后,往往会通过回调的形式来通知UI更新数据。多个线程操作经常出现foreach抛出异常:System.InvalidOperationException: 集合已修改;可能无法执行枚举操作,导致APP crash。

      在网上搜索了一下,得出以下结论:

    1.   实现一个真正线程安全的List是很困难的,具体可以参考这篇Why are thread safe collections so hard?。
    2.   使用ConcurrentBag<T>,微软给出的线程安全的集合,缺点是unordered。如果集合依赖内部元素的顺序,就不太合适了。
    3.   实现一个枚举安全的List,所需的工作量相对小很多,甚至仅需要给已用到的List操作加上lock。

      以下是一个最小化实现的枚举安全的List。因为实际工程中,需要枚举安全的集合仅用到了Add,Count,索引等操作,所以继承了IEnumerable接口,而不是IList。同时也不影响使用Linq to objects的扩展方法,真是偷了一个大懒。

        class EnumerationSafeList<T> : IEnumerable<T>
        {
            private List<T> innerList = new List<T>();
    
            private object lockObject = new object();
    
            public IEnumerator<T> GetEnumerator()
            {
                return Clone().GetEnumerator();
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return Clone().GetEnumerator();
            }
    
            public void Add(T item)
            {
                lock(lockObject)
                {
                    innerList.Add(item);
                }
            }
    
            public void Remove(T item)
            {
                lock(lockObject)
                {
                    innerList.Remove(item);
                }
            }
    
            public int Count
            {
                get
                {
                    lock(lockObject)
                    {
                        return innerList.Count;
                    }
                }
            }
    
            public T this[int index]
            {
                get
                {
                    lock(lockObject)
                    {
                        return innerList[index];
                    }
                }
                set
                {
                    lock(lockObject)
                    {
                        innerList[index] = value;
                    }
                }
            }
    
            private List<T> Clone()
            {
                var cloneList = new List<T>();
                lock(lockObject)
                {
                    foreach (var item in innerList)
                    {
                        cloneList.Add(item);
                    }
                }
                return cloneList;
            }
        }

      代码对Add,Remove,Count和索引四个操作加了lock,同时在枚举时通过加lock并返回当前集合的副本,来避免遍历时因为其他线程的修改而抛出异常。

      如果代码需要List类型的全部方法,就需要进一步修改,把IEnumerable改成IList并实现接口,就可以得到一个完整的“data thread safe list”。

      完整的代码及测试用的程序:代码

  • 相关阅读:
    2019年湘潭大学程序设计竞赛(重现赛)
    牛客练习赛43
    2251: Code Cleanups
    【软件工程】读《构建之法》
    20150401 作业2 结对 四则运算
    四则运算
    Unity3d网格合并2
    Unity网格合并_材质合并
    Unity 5 Stats窗口
    Unity3D研究院之Unity5.x运行时动态更新烘培贴图
  • 原文地址:https://www.cnblogs.com/manupstairs/p/4870187.html
Copyright © 2020-2023  润新知