最近在做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”。

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

UWP多线程枚举安全的List的更多相关文章

  1. C#多线程介绍(下)

    转载原文:这里是链接内容 转载原文:这里写链接内容 转载原文:这里写链接内容 (重要事情说三遍) 引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个 ...

  2. C#综合揭秘——细说多线程(下)

    引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发.其中委托的BeginInvoke方法以及回调函数最为常用.而 I/O线程可能 ...

  3. C#细说多线程

    引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发.其中委托的BeginInvoke方法以及回调函数最为常用.而 I/O线程可能 ...

  4. 转:C#综合揭秘——细说多线程(下)

    原文地址:http://www.cnblogs.com/leslies2/archive/2012/02/08/2320914.html 引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I ...

  5. C#细说多线程(下)

    本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发. 其中委托的BeginInvoke方法以及回调函数最为常用.而 I/O线程可能容易 ...

  6. [转]C#综合揭秘——细说多线程(下)

    引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发. 其中委托的BeginInvoke方法以及回调函数最为常用. 而 I/O线程 ...

  7. .NET 实现并行的几种方式(三)

    本随笔续接:.NET 实现并行的几种方式(二) 在前两篇随笔中,先后介绍了 Thread .ThreadPool .IAsyncResult (即 APM系列) .Task .TPL (Task Pa ...

  8. [Basic] The most basic things about java

    [Basic] The most basic things about java // */ // ]]>   [Basic] The most basic things about java ...

  9. 【转】【C#】【Thread】【Parallel】并行计算

    并行计算 沿用微软的写法,System.Threading.Tasks.Parallel类,提供对并行循环和区域的支持. 我们会用到的方法有For,ForEach,Invoke. Program.Da ...

随机推荐

  1. python博客

    https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

  2. git/github基本命令

    Git与项目 git的使用,主要包括: 本地仓库的命令 远程仓库的命令 项目需求.页面.模型类的设计,及页面的使用 sudo apt-get install git 安装成功后,运行如下命令 git ...

  3. python推荐书籍

    推荐的python电子书 python学习路线图 优先级 入门:python核心编程 提高:python cookbook 其他 (1).数据分析师 需要有深厚的数理统计基础,但是对程序开发能力不做要 ...

  4. java String标准格式转换Date(yyyy-MM-dd HH:mm:ss)

    SimpleDateFormat sdf=new SimpleDateFormat("yyy年MM月dd日hh点mm分"); Date date=new Date(); Strin ...

  5. C语言链表实现

    #define _CRT_SECURE_NO_WARNINGS #include "stdio.h" #include "stdlib.h" typedef s ...

  6. qemu模拟器下编译运行基于riscv指令集的Linux操作系统

      基本原理: 在物理服务器Ubuntu14.04上安装qemu模拟器,模拟器中运行基于riscv指令集编译的linux镜像文件. 用到的工具包括: riscv-qemu(模拟器,可以模拟运行risc ...

  7. centos7 ntp服务器配置

    一.ntp服务是什么 1. 定义 NTP是网络时间协议(Network Time Protocol),它是用来同步网络中各个计算机的时间的协议. 2. 发展 首次记载在Internet Enginee ...

  8. SQLite介绍

    优点:轻量级.绿色组件.单一文件.跨平台.查询效率极高.使用事务插入速度极快.支持limit分页. 适合查询速度要求较高,内存占用较少的场合,尤其是嵌入式操作系统,如各种手机操作系统,低并发web(9 ...

  9. golang之map数据类型

    先上代码…… package main import "fmt" func testMap() { //两种声明map方式,切记,必须初始化才能用,否则panic //var a ...

  10. Luogu 5170 【模板】类欧几里得算法

    原理不难但是写起来非常复杂的东西. 我觉得讲得非常好懂的博客.   传送门 我们设 $$f(a, b, c, n) = \sum_{i = 0}^{n}\left \lfloor \frac{ai + ...