ConcurrentBag根据操作线程,对不同线程分配不同的队列进行数据操作。这样,每个队列只有一个线程在操作,不会发生并发问题。其内部实现运用了net4.0新加入的ThreadLocal线程本地存储功能。各个队列间通过链表维护。

其内部结构如下:

1、获取线程本地队列:

 /// <summary>
/// 获取当前线程的队列
/// </summary>
/// <param name="forceCreate">如果线程没有持有队列,是否新建</param>
/// <returns></returns>
private ThreadLocalList<T> GetThreadList(bool forceCreate)
{
//尝试获取线程本地队列列表(参考ThreadLocal),此处的m_locals不同线程持有不同实例
//如果获取为空,则说明线程是第一次执行此函数,需要分配一个队列
ThreadLocalList<T> unownedList = this.m_locals.Value;
if (unownedList != null)
{
return unownedList;
}
if (forceCreate)
{
//获取当前本地队列锁,防止在冻结队列时产生冲突(参考FreezeBag函数)
object globalListsLock = this.GlobalListsLock;
lock (globalListsLock)
{
//获取本地队列
//如果没有创建过队列,则创建一个新的队列;否则尽量分配已有的线程终止的队列
if (this.m_headList == null)
{
unownedList = new ThreadLocalList<T>(Thread.CurrentThread);
this.m_headList = unownedList;
this.m_tailList = unownedList;
}
else
{
//获取无主队列,不分配新队列
unownedList = this.GetUnownedList();
if (unownedList == null)
{
unownedList = new ThreadLocalList<T>(Thread.CurrentThread);
this.m_tailList.m_nextList = unownedList;
this.m_tailList = unownedList;
}
}
this.m_locals.Value = unownedList;
return unownedList;
}
}
return null;
}

获取当前线程持有的队列

2、获取无主队列

 /// <summary>
/// 获取无主队列
/// 如果当前队列的持有线程已经终止,则为无主队列
/// </summary>
/// <returns></returns>
private ThreadLocalList<T> GetUnownedList()
{
for (ThreadLocalList<T> list = this.m_headList; list != null; list = list.m_nextList)
{
if (list.m_ownerThread.ThreadState == System.Threading.ThreadState.Stopped)
{
list.m_ownerThread = Thread.CurrentThread;
return list;
}
}
return null;
}

获取无主队列

3、插入操作代码分析

 /// <summary>
/// 向Bag添加元素
/// </summary>
/// <param name="item"></param> [__DynamicallyInvokable]
public void Add(T item)
{
//获取当前线程持有的队列
ThreadLocalList<T> threadList = this.GetThreadList(true);
//向当前持有队列添加数据
this.AddInternal(threadList, item);
} /// <summary>
/// 向队列添加数据
/// </summary>
/// <param name="list"></param>
/// <param name="item"></param>
private void AddInternal(ThreadLocalList<T> list, T item)
{
bool lockTaken = false;
try
{
//CAS原子操作,设置标志位,与Steal和Freeze实现互斥
Interlocked.Exchange(ref list.m_currentOp, );
//如果m_needSync,则说明已经发起冻结操作,需要加锁保证线程安全
if ((list.Count < ) || this.m_needSync)
{
list.m_currentOp = ;
Monitor.Enter(list, ref lockTaken);
}
list.Add(item, lockTaken);
}
finally
{
list.m_currentOp = ;
if (lockTaken)
{
Monitor.Exit(list);
}
}
}

插入操作

4、冻结Bag函数

 /// <summary>
/// 冻结Bag,不能进行增,删,获取操作
/// </summary>
/// <param name="lockTaken"></param>
private void FreezeBag(ref bool lockTaken)
{
//获取当前线程list锁
Monitor.Enter(this.GlobalListsLock, ref lockTaken);
//设置同步标志位,增,删,获取操作识别此标志位,只有获取锁才能执行
this.m_needSync = true;
//获取所有list的锁
this.AcquireAllLocks();
//等待所有操作执行完成
this.WaitAllOperations();
}

冻结bag

5、转化成数组

 /// <summary>
/// 转化为数组
/// </summary>
/// <returns></returns>
[__DynamicallyInvokable]
public T[] ToArray()
{
T[] localArray;
//没有数据返回空数组
if (this.m_headList == null)
{
return new T[];
}
bool lockTaken = false;
try
{
//冻结bag
this.FreezeBag(ref lockTaken);
//转化成List后直接转成Array
localArray = this.ToList().ToArray();
}
finally
{
this.UnfreezeBag(lockTaken);
}
return localArray;
} /// <summary>
/// 转化成list
/// </summary>
/// <returns></returns>
private List<T> ToList()
{
List<T> list = new List<T>();
//获取所有list,遍历生成副本
for (ThreadLocalList<T> list2 = this.m_headList; list2 != null; list2 = list2.m_nextList)
{
for (Node<T> node = list2.m_head; node != null; node = node.m_next)
{
list.Add(node.m_value);
}
}
return list;
}

转化成数组

[.net]ConcurrentBag源码分析的更多相关文章

  1. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  2. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  3. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  4. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  5. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  6. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  7. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  8. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  9. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

随机推荐

  1. sql多表更新使用别名(小技巧)

    update     A set     A.CityRegionID=B.ParentID,     A.CityName=(select RegionName from Common_Region ...

  2. 778. Swim in Rising Water

    ▶ 给定方阵 grid,其元素的值为 D0n-1,代表网格中该点处的高度.现在网格中开始积水,时刻 t 的时候所有值不大于 t 的格点被水淹没,当两个相邻格点(上下左右四个方向)的值都不超过 t 的时 ...

  3. 裸函数naked解析

    先分享一个案例: #include <stdio.h> __declspec(naked) void Test() { int x; x = ; __asm ret; } int main ...

  4. 操作ini配置文件设计一个最基本的可视化数据库系统

    对于很多小项目来说,不需要搭建专门的数据库系统(例如用SQLite搭建本地数据库),这时可以用ini配置文件实现一个最基本的数据库,实现数据库最基本的增删改查功能. ini配置文件的用法参考我以前写的 ...

  5. PHP半年了,已经可以独立支撑项目,几点心得记录

    从去年12开始零基础学习PHP,到现在可以独立支撑项目,感谢PHP的强大,成熟.入门容易,记录几点心得: 1.思维比什么都重要,方法要靠实践证明: 2.多写.多试,不要怕遇到坑,每一个坑都是你前进路上 ...

  6. 洛谷 P3660 [USACO17FEB]Why Did the Cow Cross the Road III G(树状数组)

    题目背景 给定长度为2N的序列,1~N各处现过2次,i第一次出现位置记为ai,第二次记为bi,求满足ai<aj<bi<bj的对数 题目描述 The layout of Farmer ...

  7. nignx reload的时候报错invalid PID number

    第一种思路是从PID号着手,提示无效PID号时nginx.pid文件为空,将进程的PID号追加到空的nginx.pid上,问题解决: 还有问题,请参考:https://www.cnblogs.com/ ...

  8. gradle使用总结

    ###打包时跳过单元测试

  9. Jenkins 邮箱配置及问题解决

    Failed to send out e-mail javax.mail.MessagingException: Could not connect to SMTP host: smtp.rytong ...

  10. Android Gson 操作

    JSON序列化后的数据不带类名与名命空间,所以这两个服务端跟客户端可以不对应,需要保证字段对应即可 Asp.net MVC端 using System; using System.Collection ...