本随笔续接:.NET 同步与异步 之 警惕闭包(十)

无论之前说的锁、原子操作 还是 警惕闭包,都是为安全保驾护航,本篇随笔继续安全方面的主题:线程安全的集合

先看一下命名空间:System.Collections.Concurrent,常用的类型有(均为泛型):BlockingCollection<T>ConcurrentBag<T>ConcurrentDictionary<TKey, TValue>ConcurrentQueue<T>ConcurrentStack<T> 。

其中:ConcurrentBag<T> 为无序的集合、ConcurrentDictionary<TKey, TValue> 为词典类型。

ConcurrentQueue<T>ConcurrentStack<T>  分别为队列 和 栈,而 BlockingCollection<T> 可以看做是 队列 和 栈的进一步封装调用,并提供了阻塞(超时)功能。

本随笔着重说两个类型:BlockingCollection<T>  和 ConcurrentDictionary<TKey, TValue>

一、BlockingCollection<T>

1、先看一下 MSDN 上的Demo

        /// <summary>
/// MSDN Demo
/// BlockingCollection<T>.Add()
/// BlockingCollection<T>.CompleteAdding()
/// BlockingCollection<T>.TryTake()
/// BlockingCollection<T>.IsCompleted
/// </summary>
public void Demo1()
{
// Construct and fill our BlockingCollection
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
int NUMITEMS = ; for (int i = ; i < NUMITEMS; i++)
{
bc.Add(i);
}
bc.CompleteAdding(); int outerSum = ; // Delegate for consuming the BlockingCollection and adding up all items
Action action = () =>
{
int localItem;
int localSum = ; while (bc.TryTake(out localItem))
{
localSum += localItem;
}
Interlocked.Add(ref outerSum, localSum);
}; // Launch three parallel actions to consume the BlockingCollection
Parallel.Invoke(action, action, action); base.PrintInfo(string.Format("Sum[0..{0}) = {1}, should be {2}", NUMITEMS, outerSum, ((NUMITEMS * (NUMITEMS - )) / )));
base.PrintInfo(string.Format("bc.IsCompleted = {0} (should be true)", bc.IsCompleted)); }
}

MSDN Demo

从demo中看一下 BlockingCollection<T> 的用法

1)Add 方法, 将项添加到集合中。

2)CompleteAdding 方法,标记当前实例不可以再添加任何项。

3)TryTake 方法,如果可以从当前集合移除一个项,则返回true,否则返回False. 如果该集合为空,则此方法立即返回 false。

  删除了某个项的顺序取决于用于创建集合的类型 BlockingCollection<T> 实例。 当您创建 BlockingCollection<T> 对象,您可以指定要使用的集合类型(通过构造函数指定)。 例如,可以指定 ConcurrentQueue<T> 先进先出 (FIFO) 行为的对象或 ConcurrentStack<T> 后进先出 (LIFO) 行为的对象。 可以使用任何集合类来实现 IProducerConsumerCollection<T> 接口。 默认集合类型 BlockingCollection<T> 是 ConcurrentQueue<T>

4)IsCompleted 属性,获取此 BlockingCollection<T> 是否已标记为完成添加(即 调用了 CompleteAdding 方法)并且为空。

2、限制容量

        /// <summary>
/// 限制容量
/// </summary>
public void Demo2()
{
BlockingCollection<int> blocking = new BlockingCollection<int>(); Task.Run(() =>
{
for (int i = ; i < ; i++)
{
blocking.Add(i);
PrintInfo($"add:({i})");
} blocking.CompleteAdding();
PrintInfo("CompleteAdding");
}); // 等待先生产数据
Task.Delay().ContinueWith((t) =>
{
while (!blocking.IsCompleted)
{
var n = ;
if (blocking.TryTake(out n))
{
PrintInfo($"TryTake:({n})");
}
} PrintInfo("IsCompleted = true");
}); }

限制容量

调研Add方法的时候,如果集合中的项的数量已经达到上限,则Add方法将会被阻塞。

3、在 BlockingCollection  中使用Stack

         /// <summary>
/// 在 BlockingCollection 中使用Stack
/// </summary>
public void Demo3()
{
BlockingCollection<int> blocking = new BlockingCollection<int>(new ConcurrentStack<int>(), ); Task.Run(() =>
{
for (int i = ; i < ; i++)
{
blocking.Add(i);
PrintInfo($"add:({i})");
} blocking.CompleteAdding();
PrintInfo("CompleteAdding");
}); // 等待先生产数据
Task.Delay().ContinueWith((t) =>
{
while (!blocking.IsCompleted)
{
var n = ;
if (blocking.TryTake(out n))
{
PrintInfo($"TryTake:({n})");
}
} PrintInfo("IsCompleted = true");
}); }

在 BlockingCollection 中使用Stack

该Demo和之前的Demo的唯一区别就是:构造函数、指定了 ConcurrentStack<T>  类型为存储容器。

除此之外、BlockingCollection<T> 还是提供了对超时的控制,例如:TryAdd(T, TimeSpan) 、 TryTake(T, TimeSpan) 等数个重载版本。

二、ConcurrentDictionary<TKey, TValue>

ConcurrentDictionary<TKey, TValue> 类, 已经实现 IDictionary<TKey, TValue> 接口。也就是说 它也实现了Dictionary 类型的基础功能。

此外, ConcurrentDictionary<TKey, TValue> 提供了几种方法中添加或更新键/值对在字典中下, 如表中所述。

任务

使用此方法

用法说明

如果它尚不在字典中存在向字典中,添加新的密钥

TryAdd

如果当前不在字典中存在该键,此方法将添加指定的键/值对。 该方法返回 true 或 false具体取决于是否已添加新对。

如果该注册表项具有特定值,更新为现有键在字典中值

TryUpdate

此方法检查是否密钥具有指定的值,如果它存在,则用新值更新该键。 它相当于CompareExchange 方法,但它的用于字典的元素。

无条件地将键/值对存储在字典中,覆盖已存在的键的值

索引器的资源库︰dictionary[key] = newValue

 

将键/值对添加到字典中,或如果键已存在,更新基于键的现有值的键的值

AddOrUpdate(TKey, Func<TKey, TValue>, Func<TKey, TValue, TValue>)

- 或 -

AddOrUpdate(TKey, TValue, Func<TKey, TValue, TValue>)

AddOrUpdate(TKey, Func<TKey, TValue>, Func<TKey, TValue, TValue>) 接受的键和两个委托。 如果键在字典; 中不存在,则使用第一个委托它接受键并返回应添加的键的值。 如果该键不存在; 它使用第二个委托它接受的键和其当前值,并返回应为项设置的新值。

AddOrUpdate(TKey, TValue, Func<TKey, TValue, TValue>) 接受键、 值要添加,以及更新委托。 这是与以前的重载中,相同之处在于它不使用委托来添加的键。

获取此键在字典中,向字典中添加值并将其返回如果该键不存在的值

GetOrAdd(TKey, TValue)

- 或 -

GetOrAdd(TKey, Func<TKey, TValue>)

这些重载提供延迟初始化为键/值对在字典中,添加的值,仅当不存在。

GetOrAdd(TKey, TValue) 采用键不存在要添加的值。

GetOrAdd(TKey, Func<TKey, TValue>) 将一个委托,可将生成的值,如果键不存在。

这些操作是原子性操作,而且都是线程安全的。在 ConcurrentDictionary<TKey, TValue> 类中 唯一的例外是 AddOrUpdate 和 GetOrAdd 方法,它们是使用细粒度锁定,以确保线程安全。

ConcurrentDictionary<TKey, TValue> 类 在上述的MSDN文档中 已经介绍的差不多了,不再举例。 当然还要提一句, 该类型不支持阻塞操作。

三、线程安全警告

在命名空间 System.Collections.Concurrent 中的类型,都遵循如下的线程安全规则:线程安全集合本身提供的方法 是线程安全的,但是通过其类型实现的接口的方法 和 扩展方法 却不是线程安全的。

附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip

参见更多:随笔导读:同步与异步

.NET 同步与异步 之 线程安全的集合 (十一)的更多相关文章

  1. .NET 同步与异步 之 Mutex (十二)

    本随笔续接:.NET 同步与异步 之 线程安全的集合 (十一) 本随笔 及 接下来的两篇随笔,将介绍 .NET 同步与异步系列 的最后一个大块知识点:WaitHandle家族. 抽象基类:WaitHa ...

  2. C++11 半同步半异步线程池的实现

    #include <list> #include <mutex> #include <thread> #include <condition_variable ...

  3. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...

  4. CIL锁,GIL与线程池的区别,进程池和线程池,同步与异步

    一.GIL锁 什么是GIL? 全局解释器锁,是加在解释器上的互斥锁 GC是python自带的内存管理机制,GC的工作原理:python中的内存管理使用的是应用计数,每个数会被加上一个整型的计数器,表示 ...

  5. spring线程池(同步、异步)

    一.spring异步线程池类图 二.简单介绍 2.1. TaskExecutor---Spring异步线程池的接口类,其实质是java.util.concurrent.Executor 以下是官方已经 ...

  6. [多线程]线程基础(对象锁、class锁、同步、异步)

    synchronized.volatile.ReentrantLock.concurrent 线程安全:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法) ...

  7. Python线程,进程,携程,I/O同步,异步

    只有本人能看懂的-Python线程,进程,携程,I/O同步,异步 举个栗子: 我想get三个url,先用普通的for循环 import requests from multiprocessing im ...

  8. java 线程之对象的同步和异步

    一.多线程环境下的同步与异步 同步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去. package com.jalja.org.th ...

  9. 使用C++11 开发一个半同步半异步线程池

    摘自:<深入应用C++11>第九章 实际中,主要有两种方法处理大量的并发任务,一种是一个请求由系统产生一个相应的处理请求的线程(一对一) 另外一种是系统预先生成一些用于处理请求的进程,当请 ...

随机推荐

  1. 2018WFU校赛B题

    我们在ACM的题目中已经了解了什么是ACM了,ACM还是很残酷的了(ಥ _ ಥ),那么现在你就要解决一个ACM最简单的题了,简单到省赛和区域赛都不会出这种简单的题.ls很强,即使每年都在ACM这个大坑 ...

  2. HDU 1010 Tempter of the Bone (DFS+可行性奇偶剪枝)

    <题目链接> 题目大意:一个迷宫,给定一个起点和终点,以及一些障碍物,所有的点走过一次后就不能再走(该点会下陷).现在问你,是否能从起点在时间恰好为t的时候走到终点. 解题分析:本题恰好要 ...

  3. hdu 2364 Escape【模拟优先队列】【bfs】

    题目链接:https://vjudge.net/contest/184966#problem/A 题目大意: 走迷宫.从某个方向进入某点,优先走左或是右.如果左右都走不通,再考虑向前.绝对不能往后走, ...

  4. chrome刷新CSS

    改动CSS发现页面根本没有变化,再三查看确实是这一处CSS,那么可能的就是浏览器缓存了CSS而刷新无效了. chrome刷新CSS: 方法1:直接ctrl+F5,进行强制刷新页面,浏览器会重新加载所有 ...

  5. Springmvc借助SimpleUrlHandlerMapping实现接口开关功能

    一.接口开关功能 1.可配置化,依赖配置中心 2.接口访问权限可控 3.springmvc不会扫描到,即不会直接的将接口暴露出去 二.接口开关使用场景 和业务没什么关系,主要方便查询系统中的一些状态信 ...

  6. mybatis之级联关系(一对一、一对多)

    0. 表结构 1. 准备工作 1.1 配置文件等信息,请参考  myBatis之入门示例 1.2 entity 1.2.1 TPersonInfo.java package com.blueStarW ...

  7. 使用CCS调试基于AM335X的SPL、Uboot(原创)

    使用CCS调试基于AM335X的SPL.Uboot 一.开发环境 1.硬件平台:创龙AM3359核心板 2.SDK版本:ti-processor-sdk-linux-am335x-evm-03.00. ...

  8. Alpha(9/10)

    鐵鍋燉腯鱻 项目:小鱼记账 团队成员 项目燃尽图 冲刺情况描述 站立式会议照片 各成员情况 团队成员 学号 姓名 git地址 博客地址 031602240 许郁杨 (组长) https://githu ...

  9. C#Workbooks 对象的 Open 方法参数说明

    打开一个工作簿. excelApp.Open(FileName, UpdateLinks, ReadOnly, Format, Password, WriteResPassword,IgnoreRea ...

  10. 服务端spark gbdt模型计算性能优化

    服务端使用训练出来的模型,spark模型计算第一步是实现spark模型加载. 线上服务对用户体验影响极大,故需要对模型使用进行优化. 1.多线程并发进行计算,线上两个服务.优化cpu 2.在扩召回集, ...