[.net]ConcurrentBag源码分析
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源码分析的更多相关文章
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
- ABP源码分析二:ABP中配置的注册和初始化
一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
随机推荐
- django-redis缓存
1.安装django依赖包 pip install djange-redis==4.8.0 2.配置文件settings 需要开启redis服务 sudo service redis start,否 ...
- 好记性不如烂笔头-linux学习笔记3mysql数据库导入导出
1 数据库文件导出 mysqldump -uroot -p123456 test > 1.sql 2数据库文件导入 mysql -uroot -p123456 test <1.sql 3 ...
- OpenMP 简单的规约
▶ 简单的计算和规约 ● 第一种方法,将全局和的指针传入工作函数中进行加和,使用 critical 来控制临界区的访问 #include <stdio.h> #include <st ...
- 使用http-proxy-middleware 代理跨域
使用http-proxy-middleware 代理跨域 例如请求的url:“http://f.apiplus.cn/bj11x5.json” 1.打开config/index.js,在proxyTa ...
- Javascript typeof 用法
在js里用到数组,比如 多个名字相同的input, 若是动态生成的, 提交时就需要判断其是否是数组. if(document.mylist.length != "undefined" ...
- C#实现图片文件到数据流再到图片文件的转换
//----引入必要的命名空间using System.IO;using System.Drawing.Imaging; //----代码部分----// private byte[] photo;/ ...
- leetcode190
public class Solution { public uint reverseBits(uint n) { var list = new List<uint>();//逆序的二进制 ...
- Java常用的转义字符
以下为常用的转义字符对照表: 字母前面加上捺斜线"\"来表示常见的那些不能显示的ASCII字符.称为转义字符.如\0,\t,\n等,就称为转义字符. 转义字符 意义 ASCII码值 ...
- 2 数据库开发--MySQL下载(windows)
Windows:(mysql) 操作: 0.下载安装mysql www.mysql.org downloads->进入社区community community 5.7.21 下载5.6 Mic ...
- matplotlib —— 添加文本信息(text)
[详细]http://hyry.dip.jp/tech/book/page/scipy/matplotlib_fast_plot.html http://blog.csdn.net/lanchunhu ...