[.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 ...
随机推荐
- Centos编译Redis4.0.9源码过程记录
mkdir /home/redis cd /home/redis 下载源码 wget https://codeload.github.com/antirez/redis/tar/4.0.9 解压源码包 ...
- C++中构造函数作用
一. 构造函数是干什么的 class Counter { public: // 类Counter的构造函数 // 特点:以类名作为函数名,无返回类型 Counter() { m_value = ; } ...
- VS2017连接Oracle设置
1. 下载安装 Oracle Developer Tools for Visual Studio 2017: 2. 配置tnsnames.ora ODP.Net默认使用安装目录下的 tnsnames. ...
- 「小程序JAVA实战」小程序数据缓存API(54)
转自:https://idig8.com/2018/09/22/xiaochengxujavashizhanxiaochengxushujuhuancunapi52/ 刚开始写小程序的时候,用户信息我 ...
- 怎么改变Eclipse中项目的运行服务器
eclipse中-->选定项目-->右键-->弹出 Properties for项目名 -->选择server子项-->选择需要的server即可(Always use ...
- MVC表单提交写法1
初学MVC,感觉跟以前的aspx页面差别很大,我们就先来看看MVC的表单是怎么提交的吧. 现在我们用一个最简单的例子来看一看MVC是怎么提交表单的(这一个例子中,我们的关注点是如何提交表单,所以不涉及 ...
- Python3 sorted() 函数
Python3 sorted() 函数 Python3 内置函数 描述 sorted() 函数对所有可迭代的对象进行排序操作. sort 与 sorted 区别: sort 是应用在 list 上的 ...
- 使用mybatis提供的各种标签方法实现动态拼接Sql。使用sql片段提取重复的标签内容
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的,如下: <select id="findUserByNameAndSex" par ...
- 一个合约访问另一个合约中的mapping
参考链接:https://ethereum.stackexchange.com/questions/13616/accessing-a-public-mapping-within-a-contract ...
- CF 990 Educational Codeforces Round 45
既然补了就简单记录一下. 感觉还算有一点营养. 官方题解传送门:点我 A Commentary Boxes 对拆掉$n \mod m$个和新建$m - (n \mod m)$求个最小. #includ ...