事件深入分析,托管堆在运维时的涉及
分类:long8

事件订阅者的平地风波微处理机必需和这么些限定相对应才可以订阅那几个事件,其他方面,事件订阅者收到事件随后做出事件微机,而这一个事件微处理器必得通过信托才方可做到。

3.初阶化类型对象指针(指向类型对象卡塔 尔(阿拉伯语:قطر‎和联合块索引

  相同的从IDemo<object>类型向IDemo<string>类型的调换,然而在这里地大家却将父类型隐式调换为子类型的用法叫逆变。(这里不可不要用in卡塔 尔(阿拉伯语:قطر‎

并发编制程序的规划原理

大部面世编制程序本领有三个相近点:它们本质上都以函数式(functional卡塔 尔(阿拉伯语:قطر‎的。函数式编制程序思想是出新编程的面目。

我水平有限,就算不当招待各位争论指正!

附上ConcurrentBag<T>源码地址:戳一戳

目的或然类具备布告手艺的分子。比方说手提式有线电话机接到短信提示自身去开会,那么手提式有线电话机就担当了一个颇负布告工夫的成员。说白了,事件

图片 1

 interface IDemo<in T>
    {
        string Method(T str);
    }
    class One : IDemo<string>
    {
        public string Method(string str)
        {
            return str;
        }
    }
    class Two : IDemo<object>
    {
        public string Method(object str)
        {
            return str.ToString();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            IDemo<object> two = new Two();
            IDemo<string> one;
            one = two;
            Console.WriteLine(one.Method("2222"));
        }
    }

互相的三种情势

彼此编制程序的施用意况:需求实施大气的简政放权职分,并且那些职务能分开成相互独立的职分块儿

相互的款型有三种:数据人机联作(data parallelism卡塔尔国和天职并行(task parallelim卡塔 尔(阿拉伯语:قطر‎。

数码交互作用(data parallelism卡塔 尔(阿拉伯语:قطر‎:有雅量的数量要求管理,何况每一块数据的管理进程基本上是相互独立的。

任务并行(task parallelim卡塔尔国:要求施行大气职责,并且每种职分的施行过程基本上是相互独立的。任务并行能够是动态的,假设叁个职分的实践结果会时有发生额外的天职,这么些新扩大的天职也得以步向职责池。

福寿双全数据人机联作的艺术

  • Parallel.ForEach
  • PLINQ(Parallel LINQ)

每一个职务块要硬着头皮的相互作用独立。 只要任务块是相互独立的,并行性就能够幸不辱命最大化。后生可畏旦您在多少个线程中国共产党享状态,就一定要以三头方式访问那一个意况,那样程序的并行性就变差了。

多少交互作用重视在管理多少,任务并行则保养执行职分。

兑现职分并行的点子

  • Parallel.Invoke
  • Task.Wait

平时情形下,没须求关切线程池处理任务的具体做法。数据交互作用和天职并行都施用动态调解的分割器,把职责分割后分配给办事线程。线程池在要求的时候会追加线程数量。线程池线程使用职业盗取队列(work-stealing queue卡塔 尔(英语:State of Qatar)。

事件深入分析,托管堆在运维时的涉及。三、 ConcurrentBag线程安全达成原理

奉公守法事件的四个成分,首先须要事件源,做三个Customer类,还须要一个风云订阅者,做多少个Waitor类,然后依据订阅关系去写现实的点子,订阅关系customer.Order += waitor.Serve; Customer点餐Waitor服务,waitor类中上餐并算好价钱,这时候供给叁个事件微型机Order伊夫ntHandler,那一个委托的参数必要叁个OrderEventArgs,成立这些类写好属性,在写好委托和事件,然后在Customer类中写点餐事件,点餐事件为Protected的,和public型的嘱托字段相通幸免被外边滥用,进步安全性。

2.在托管堆上分配第一步长度的空中

  先来举个为主的例证,来增长你对可变性的领会。在C#中有隐式类型转变,举例:

TPL数据流

异步编制程序和相互影响编制程序那二种本事结合起来就是TPL数据流
数码流网格的中央组成单元是数据流块(dataflow block卡塔 尔(阿拉伯语:قطر‎。

PAJEROx 和 TPL有那多少个相似点。
网格和流都有“数据项”这一定义,数据项从网格或流的中档穿过。还应该有,网格和流都有“寻常达成”(表示未有越多数据须求抽出时发生的通告卡塔尔国和“不健康完结”(在拍卖多少中生出错误时发出的布告卡塔 尔(英语:State of Qatar)这三个概念。但是,RAV4x 和 TPL 数据流的属性并不相像。

当要求实行必要计时的任务,最棒选用是奥德赛x的 可旁观流 observable 对象
当需求实行并行管理,最好选项是 TPL数据流块

目录

二、事件:

如图:

 interface IDemo<out T>
    {
        T Method(string str);
    }
    class One : IDemo<string>
    {
        public string Method(string str)
        {
            return str;
        }
    }
    class Two : IDemo<object>
    {
        public object Method(string str)
        {
            return str;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            IDemo<string> one = new One();
            IDemo<object> two;
            two = one;
            Console.WriteLine(two.Method("2222"));
        }
    }

线程和线程池

线程是二个单身的周转单元,每个进度之中有八个线程,种种线程能够独家同期实行命令。各个线程有谈得来单独的栈,可是与经过内的别的线程共享内部存储器。
对一些程序来讲,个中有贰个线程是例外的,例如客户界面程序有二个 UI 线程,调控台程序有二个 main 线程。

各类 .NET 程序都有三个线程池,线程池维护着自然数量的行事线程,那一个线程等待着实行分配下去的任务。线程池可以每日监测线程的数码。配置线程池的参数多达几13个,可是建议选择暗中认可设置,线程池的暗中同意设置是由此留意调解的,适用于繁多实际中的应用项景。


本节对事件举办总计。

图片 2

  ①:协变即为在泛型接口类型中采纳out标记的门类参数。协变的字面意思是“与调换的方向同样”②逆变那就是用in来标记的泛型接口类型的品类参数。逆变的字面意思是“与变化的大方向相反”

响应式编制程序Evoquex学习难度异常的大

运用境况:管理的事件中隐含参数,最好使用响应式编制程序
响应式编程的主旨概念是:可观察的流(observable stream卡塔 尔(英语:State of Qatar)
响应式编程的最终代码特别像 LINQ,能够以为它就是“LINQ to events”,它采纳“推送”情势,事件达到后就活动通过查询。

4. ConcurrentBag 怎么样贯彻迭代器形式

看完上面的代码后,小编很奇怪ConcurrentBag<T>是什么样贯彻IEnumerator来促成迭代访谈的,因为ConcurrentBag<T>是透过分散在分化线程中的ThreadLocalList来积攒数据的,那么在落到实处迭代器形式时,进度会比较复杂。

前面再查看了源码之后,发掘ConcurrentBag<T>为了达成迭代器情势,将分在区别线程中的数据全都存到八个List<T>聚拢中,然后回来了该别本的迭代器。所以每一遍访谈迭代器,它都会新建二个List<T>的别本,那样尽管浪费了迟早的寄存空间,可是逻辑上越来越简明了。

/// <summary>
/// 本地帮助器方法释放所有本地列表锁
/// </summary>
private void ReleaseAllLocks()
{
    // 该方法用于在执行线程同步以后 释放掉所有本地锁
    // 通过遍历每个线程中存储的 ThreadLocalList对象 释放所占用的锁
    ThreadLocalList currentList = m_headList;
    while (currentList != null)
    {

        if (currentList.m_lockTaken)
        {
            currentList.m_lockTaken = false;
            Monitor.Exit(currentList);
        }
        currentList = currentList.m_nextList;
    }
}

/// <summary>
/// 从冻结状态解冻包的本地帮助器方法
/// </summary>
/// <param name="lockTaken">The lock taken result from the Freeze method</param>
private void UnfreezeBag(bool lockTaken)
{
    // 首先释放掉 每个线程中 本地变量的锁
    // 然后释放全局锁
    ReleaseAllLocks();
    m_needSync = false;
    if (lockTaken)
    {
        Monitor.Exit(GlobalListsLock);
    }
}

/// <summary>
/// 本地帮助器函数等待所有未同步的操作
/// </summary>
private void WaitAllOperations()
{
    Contract.Assert(Monitor.IsEntered(GlobalListsLock));

    ThreadLocalList currentList = m_headList;
    // 自旋等待 等待其它操作完成
    while (currentList != null)
    {
        if (currentList.m_currentOp != (int)ListOperation.None)
        {
            SpinWait spinner = new SpinWait();
            // 有其它线程进行操作时,会将cuurentOp 设置成 正在操作的枚举
            while (currentList.m_currentOp != (int)ListOperation.None)
            {
                spinner.SpinOnce();
            }
        }
        currentList = currentList.m_nextList;
    }
}

/// <summary>
/// 本地帮助器方法获取所有本地列表锁
/// </summary>
private void AcquireAllLocks()
{
    Contract.Assert(Monitor.IsEntered(GlobalListsLock));

    bool lockTaken = false;
    ThreadLocalList currentList = m_headList;

    // 遍历每个线程的ThreadLocalList 然后获取对应ThreadLocalList的锁
    while (currentList != null)
    {
        // 尝试/最后 bllock 以避免在获取锁和设置所采取的标志之间的线程港口
        try
        {
            Monitor.Enter(currentList, ref lockTaken);
        }
        finally
        {
            if (lockTaken)
            {
                currentList.m_lockTaken = true;
                lockTaken = false;
            }
        }
        currentList = currentList.m_nextList;
    }
}

/// <summary>
/// Local helper method to freeze all bag operations, it
/// 1- Acquire the global lock to prevent any other thread to freeze the bag, and also new new thread can be added
/// to the dictionary
/// 2- Then Acquire all local lists locks to prevent steal and synchronized operations
/// 3- Wait for all un-synchronized operations to be done
/// </summary>
/// <param name="lockTaken">Retrieve the lock taken result for the global lock, to be passed to Unfreeze method</param>
private void FreezeBag(ref bool lockTaken)
{
    Contract.Assert(!Monitor.IsEntered(GlobalListsLock));

    // 全局锁定可安全地防止多线程调用计数和损坏 m_needSync
    Monitor.Enter(GlobalListsLock, ref lockTaken);

    // 这将强制同步任何将来的添加/执行操作
    m_needSync = true;

    // 获取所有列表的锁
    AcquireAllLocks();

    // 等待所有操作完成
    WaitAllOperations();
}

/// <summary>
/// 本地帮助器函数返回列表中的包项, 这主要由 CopyTo 和 ToArray 使用。
/// 这不是线程安全, 应该被称为冻结/解冻袋块
/// 本方法是私有的 只有使用 Freeze/UnFreeze之后才是安全的 
/// </summary>
/// <returns>List the contains the bag items</returns>
private List<T> ToList()
{
    Contract.Assert(Monitor.IsEntered(GlobalListsLock));
    // 创建一个新的List
    List<T> list = new List<T>();
    ThreadLocalList currentList = m_headList;
    // 遍历每个线程中的ThreadLocalList 将里面的Node的数据 添加到list中
    while (currentList != null)
    {
        Node currentNode = currentList.m_head;
        while (currentNode != null)
        {
            list.Add(currentNode.m_value);
            currentNode = currentNode.m_next;
        }
        currentList = currentList.m_nextList;
    }

    return list;
}

/// <summary>
/// Returns an enumerator that iterates through the <see
/// cref="ConcurrentBag{T}"/>.
/// </summary>
/// <returns>An enumerator for the contents of the <see
/// cref="ConcurrentBag{T}"/>.</returns>
/// <remarks>
/// The enumeration represents a moment-in-time snapshot of the contents
/// of the bag.  It does not reflect any updates to the collection after 
/// <see cref="GetEnumerator"/> was called.  The enumerator is safe to use
/// concurrently with reads from and writes to the bag.
/// </remarks>
public IEnumerator<T> GetEnumerator()
{
    // Short path if the bag is empty
    if (m_headList == null)
        return new List<T>().GetEnumerator(); // empty list

    bool lockTaken = false;
    try
    {
        // 首先冻结整个 ConcurrentBag集合
        FreezeBag(ref lockTaken);
        // 然后ToList 再拿到 List的 IEnumerator
        return ToList().GetEnumerator();
    }
    finally
    {
        UnfreezeBag(lockTaken);
    }
}

由地点的代码可明白,为了拿走迭代器对象,总共实行了三步关键的操作。

  1. 使用FreezeBag()主意,冻结一切ConcurrentBag<T>聚拢。因为必要扭转集结的List<T>别本,生成副这个时候期不可能有此外线程改正损坏数据。
  2. ConcurrrentBag<T>生成List<T>副本。因为ConcurrentBag<T>积存数据的办法比较优异,直接完结迭代器情势困难,思忖到线程安全和逻辑,最棒的主意是生成三个别本。
  3. 成功以上操作之后,就足以选用UnfreezeBag()办法解冻整个群集。

那么FreezeBag()措施是何许来冻结一切集结的吗?也是分为三步走。

  1. 先是获得全局锁,通过Monitor.Enter(GlobalListsLock, ref lockTaken);如此那般一条语句,那样任何线程就无法冻结集合。
  2. 接下来拿走具有线程中ThreadLocalList的锁,通过`AcquireAllLocks()方法来遍历获取。那样任何线程就无法对它实行操作损坏数据。
  3. 等候已经步向了操作流程线程停止,通过WaitAllOperations()情势来兑现,该方法会遍历每叁个ThreadLocalList对象的m_currentOp属性,确认保证全部地处None操作。

造成以上流程后,那么正是真的的冻结了一切ConcurrentBag<T>汇聚,要解冻的话也周围。在这里不再赘言。

 

}

比方:

异步编制程序的多少个好处

  1. 对此面向终端顾客的 GUI 程序:异步编制程序升高了响应技术。面临在运营时被暂且锁定分界面包车型地铁程序,异步编制程序能够使程序在这里刻仍是可以流利的响应客商的输入。比方:WPF分界面,施行一个亟待静观其变的操作时,还能够点击输入框举行填空,而不会并发卡顿,不可能点击的动静或许对页面不可能進展拖拽。
  2. 对于服务器端应用:异步编制程序完成了可扩充性。服务器应用能够利用线程池知足其可扩张性,使用异步编制程序后,可扩大性寒日能够巩固一个数额级。即加强服务器端应用的TPS(Transactions Per Second卡塔尔国和 QPS (Queries Per Second卡塔 尔(阿拉伯语:قطر‎

2. 用以数据存款和储蓄的TrehadLocalList类

接下去大家来看一下ThreadLocalList类的布局,该类正是实在存款和储蓄了数码的职位。实际上它是选取双向链表这种结构实行数量存储。

[Serializable]
// 构造了双向链表的节点
internal class Node
{
    public Node(T value)
    {
        m_value = value;
    }
    public readonly T m_value;
    public Node m_next;
    public Node m_prev;
}

/// <summary>
/// 集合操作类型
/// </summary>
internal enum ListOperation
{
    None,
    Add,
    Take
};

/// <summary>
/// 线程锁定的类
/// </summary>
internal class ThreadLocalList
{
    // 双向链表的头结点 如果为null那么表示链表为空
    internal volatile Node m_head;

    // 双向链表的尾节点
    private volatile Node m_tail;

    // 定义当前对List进行操作的种类 
    // 与前面的 ListOperation 相对应
    internal volatile int m_currentOp;

    // 这个列表元素的计数
    private int m_count;

    // The stealing count
    // 这个不是特别理解 好像是在本地列表中 删除某个Node 以后的计数
    internal int m_stealCount;

    // 下一个列表 可能会在其它线程中
    internal volatile ThreadLocalList m_nextList;

    // 设定锁定是否已进行
    internal bool m_lockTaken;

    // The owner thread for this list
    internal Thread m_ownerThread;

    // 列表的版本,只有当列表从空变为非空统计是底层
    internal volatile int m_version;

    /// <summary>
    /// ThreadLocalList 构造器
    /// </summary>
    /// <param name="ownerThread">拥有这个集合的线程</param>
    internal ThreadLocalList(Thread ownerThread)
    {
        m_ownerThread = ownerThread;
    }
    /// <summary>
    /// 添加一个新的item到链表首部
    /// </summary>
    /// <param name="item">The item to add.</param>
    /// <param name="updateCount">是否更新计数.</param>
    internal void Add(T item, bool updateCount)
    {
        checked
        {
            m_count++;
        }
        Node node = new Node(item);
        if (m_head == null)
        {
            Debug.Assert(m_tail == null);
            m_head = node;
            m_tail = node;
            m_version++; // 因为进行初始化了,所以将空状态改为非空状态
        }
        else
        {
            // 使用头插法 将新的元素插入链表
            node.m_next = m_head;
            m_head.m_prev = node;
            m_head = node;
        }
        if (updateCount) // 更新计数以避免此添加同步时溢出
        {
            m_count = m_count - m_stealCount;
            m_stealCount = 0;
        }
    }

    /// <summary>
    /// 从列表的头部删除一个item
    /// </summary>
    /// <param name="result">The removed item</param>
    internal void Remove(out T result)
    {
        // 双向链表删除头结点数据的流程
        Debug.Assert(m_head != null);
        Node head = m_head;
        m_head = m_head.m_next;
        if (m_head != null)
        {
            m_head.m_prev = null;
        }
        else
        {
            m_tail = null;
        }
        m_count--;
        result = head.m_value;

    }

    /// <summary>
    /// 返回列表头部的元素
    /// </summary>
    /// <param name="result">the peeked item</param>
    /// <returns>True if succeeded, false otherwise</returns>
    internal bool Peek(out T result)
    {
        Node head = m_head;
        if (head != null)
        {
            result = head.m_value;
            return true;
        }
        result = default(T);
        return false;
    }

    /// <summary>
    /// 从列表的尾部获取一个item
    /// </summary>
    /// <param name="result">the removed item</param>
    /// <param name="remove">remove or peek flag</param>
    internal void Steal(out T result, bool remove)
    {
        Node tail = m_tail;
        Debug.Assert(tail != null);
        if (remove) // Take operation
        {
            m_tail = m_tail.m_prev;
            if (m_tail != null)
            {
                m_tail.m_next = null;
            }
            else
            {
                m_head = null;
            }
            // Increment the steal count
            m_stealCount++;
        }
        result = tail.m_value;
    }


    /// <summary>
    /// 获取总计列表计数, 它不是线程安全的, 如果同时调用它, 则可能提供不正确的计数
    /// </summary>
    internal int Count
    {
        get
        {
            return m_count - m_stealCount;
        }
    }
}

从地点的代码中大家能够进一层证实早先的意见,便是ConcurentBag<T>在叁个线程中蕴藏数据时,使用的是双向链表ThreadLocalList兑现了风流倜傥组对链表增加和删除改查的秘籍。

Example:举三个买主在KFC点餐的事例

namespace ConsoleApp15
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            Waitor waitor = new Waitor();
            customer.Order += waitor.Serve;
            customer.Eat();
            customer.Pay();
        }
    }
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
    public class Customer
    {
        public int Money { get; set; }
        public event OrderEventHandler Order;
        public void Pay()
        {
            Console.WriteLine($"OK,{Money} dollars");
        }
        public void Eat()
        {
            Console.WriteLine("Let's go to the KFC...");
            Console.WriteLine("Stand in front of the waitor...");
            Console.WriteLine("A hamburger,Please...");
            OnOrder();
        }
        protected void OnOrder()
        {
            OrderEventArgs orderEventArgs = new OrderEventArgs();
            orderEventArgs.Snack = "Hamburger";
            orderEventArgs.Size = "large";
            this.Order.Invoke(this, orderEventArgs);

        }
    }
    public class OrderEventArgs : EventArgs
    {
        public string Snack { get; set; }
        public string Size { get; set; }
    }
    class Waitor
    {
        public void Serve(Customer customer, OrderEventArgs e)
        {
            Console.WriteLine($"Here is your snack {e.Snack}");
            int price = 20;
            switch (e.Size)
            {
                case "large":
                    price *= 2;
                    break;
                case "small":
                    price *= 1;
                    break;
                default:
                    break;
            }
            customer.Money += price;
        }
    }
}

  e=new Manager();

  今日追思了事先看的《深切通晓C#》那本书中的泛型章节,当中对泛型的可变性的精晓。泛型可变性分二种:协变和逆变。逆变也又称之为抗变。

并发编制程序的术语

  • 并发
    还要做多件职业
  • 多线程
    并发的生龙活虎种样式,它使用三个线程来实施顺序。
    多线程是出新的豆蔻梢头种样式,但不是唯风流倜傥的款型。
  • 并行管理
    把正在推行的大气的任务分割成小块,分配给多个同期运维的线程。
    并行管理是十六线程的大器晚成种,而八线程是出现的大器晚成种。
  • 异步编制程序
    并发的风度翩翩种情势,它使用future形式或回调(callback卡塔尔国机制,以幸免生出不必要的线程。
    多少个 future(或 promise卡塔尔国类型代表有个别就要成功的操作。在 .NET 中,新版 future 类型有 Task 和 Task 。在老式异步编制程序 API 中,选取回调或事件(event卡塔尔国,并不是future。异步编制程序的核心境念是异步操作(asynchronous operation卡塔 尔(阿拉伯语:قطر‎:运转了的操作将会在乎气风发段时间后成功。那么些操作正在推行时,不会卡住原来的线程。运转了那一个操作的线程,能够继续推行别的职务。当操作完结时,会打招呼它的 future,只怕调用回调函数,以便让程序知道操作已经终止。
  • 响应式编制程序
    一种注解式的编制程序情势,程序在该形式中对事件做出响应。
    响应式编制程序的宗旨情念是异步事件(asynchronous event卡塔 尔(阿拉伯语:قطر‎:异步事件能够未有三个其实的“开始”,能够在其余时间发生,并且可以生出高频,举例顾客输入。
    只要把叁个程序当作一个大型的状态机,则该程序的表现便可身为它对一文山会海事件做出响应,即每换二个平地风波,它就立异一回和睦的事态。

四、总结

上边给出一张图,描述了ConcurrentBag<T>是何等存款和储蓄数据的。通过种种线程中的ThreadLocal来实现线程本地存款和储蓄,每一种线程中皆有这么的协会,互不困扰。然后每种线程中的m_headList老是指向ConcurrentBag<T>的首先个列表,m_tailList本着最终三个列表。列表与列表之间通过m_locals 下的 m_nextList四处,构成八个单链表。

多少存款和储蓄在种种线程的m_locals中,通过Node类构成三个双向链表。
PS: 要注意m_tailListm_headList并非积攒在ThreadLocal中,而是兼具的线程分享生龙活虎份。

图片 3

以上便是有关ConcurrentBag<T>类的兑现,作者的意气风发部分笔录和深入分析。

此处举的例证正是windowsforms内部的代码,大家说事件小编是不会发生的是由事件源内部的逻辑所接触,在本例中,并非人按了开关然后按键触发了事件,

internal class Employee

 怎么掌握那四个名词的意趣:

一、前言

小编目前在做叁个门类,项目中为了进步吞吐量,使用了音信队列,中间实现了分娩花费方式,在临盆花销者情势中必要有三个会晤,来存款和储蓄生产者所生育的物品,小编利用了最广大的List<T>聚拢类型。

出于分娩者线程有众七个,花费者线程也可以有众多少个,所以不可制止的就产生了线程同步的标题。开端作者是采用lock重要字,进行线程同步,但是品质并非刻意卓越,然后有网上朋友说能够利用SynchronizedList<T>来顶替使用List<T>达到线程安全的指标。于是小编就替换到了SynchronizedList<T>,不过开采品质照旧不好,于是查看了SynchronizedList<T>的源代码,发掘它就是粗略的在List<T>提供的API的底工上加了lock,所以品质基本与笔者达成格局八九不离十。

最后作者找到了解决的方案,使用ConcurrentBag<T>类来贯彻,品质有相当大的改动,于是我查阅了ConcurrentBag<T>的源代码,达成丰盛精致,特此在此记录一下。

1、概念:Event:A member that enables an object or class to provide notifications;官方的解释是那般,就是说在C#中,事件是使

{

反倒的大家在来看一下有关逆变的代码:

1. ConcurrentBag的私家字段

ConcurrentBag线程安全完毕注重是透过它的数码存款和储蓄的协会和细颗粒度的锁。

   public class ConcurrentBag<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>
    {
        // ThreadLocalList对象包含每个线程的数据
        ThreadLocal<ThreadLocalList> m_locals;

        // 这个头指针和尾指针指向中的第一个和最后一个本地列表,这些本地列表分散在不同线程中
        // 允许在线程局部对象上枚举
        volatile ThreadLocalList m_headList, m_tailList;

        // 这个标志是告知操作线程必须同步操作
        // 在GlobalListsLock 锁中 设置
        bool m_needSync;

}

首推大家来看它表明的私家字段,个中必要静心的是集聚的数量是存放在在ThreadLocal线程本地存款和储蓄中的。也等于说访问它的每种线程会维护二个谈得来的集合数据列表,一个成团中的数据大概会贮存在不一样线程的地面存款和储蓄空间中,所以只要线程访谈本人本地存款和储蓄的靶子,那么是从未有过难点的,这正是完结线程安全的首先层,使用线程本地存款和储蓄数据

然后能够看看ThreadLocalList m_headList, m_tailList;其一是存放在着本地列表对象的头指针和尾指针,通过那八个指针,大家就足以由此遍历的艺术来访谈具备地方列表。它应用volatile修饰,不容许线程实行地面缓存,各个线程的读写都以直接操作在分享内部存款和储蓄器上,那就确认保证了变量始终具备风姿浪漫致性。任何线程在别的时刻开展读写操作均是最新值。对于volatile修饰符,感谢自家是技术员建议描述不当。

末尾又定义了二个阐明,那么些标识告知操作线程必需进行同步操作,那是完毕了叁个细颗粒度的锁,因为唯有在多少个原则满意的动静下才供给张开线程同步。

做客品级Protected。只怕有一点点蒙,举个实例就懂了。

  public int32 M1(){.....};

  

3. ConcurrentBag落到实处新添成分

接下去大家看大器晚成看ConcurentBag<T>是如何新增新币素的。

/// <summary>
/// 尝试获取无主列表,无主列表是指线程已经被暂停或者终止,但是集合中的部分数据还存储在那里
/// 这是避免内存泄漏的方法
/// </summary>
/// <returns></returns>
private ThreadLocalList GetUnownedList()
{
    //此时必须持有全局锁
    Contract.Assert(Monitor.IsEntered(GlobalListsLock));

    // 从头线程列表开始枚举 找到那些已经被关闭的线程
    // 将它所在的列表对象 返回
    ThreadLocalList currentList = m_headList;
    while (currentList != null)
    {
        if (currentList.m_ownerThread.ThreadState == System.Threading.ThreadState.Stopped)
        {
            currentList.m_ownerThread = Thread.CurrentThread; // the caller should acquire a lock to make this line thread safe
            return currentList;
        }
        currentList = currentList.m_nextList;
    }
    return null;
}
/// <summary>
/// 本地帮助方法,通过线程对象检索线程线程本地列表
/// </summary>
/// <param name="forceCreate">如果列表不存在,那么创建新列表</param>
/// <returns>The local list object</returns>
private ThreadLocalList GetThreadList(bool forceCreate)
{
    ThreadLocalList list = m_locals.Value;

    if (list != null)
    {
        return list;
    }
    else if (forceCreate)
    {
        // 获取用于更新操作的 m_tailList 锁
        lock (GlobalListsLock)
        {
            // 如果头列表等于空,那么说明集合中还没有元素
            // 直接创建一个新的
            if (m_headList == null)
            {
                list = new ThreadLocalList(Thread.CurrentThread);
                m_headList = list;
                m_tailList = list;
            }
            else
            {
               // ConcurrentBag内的数据是以双向链表的形式分散存储在各个线程的本地区域中
                // 通过下面这个方法 可以找到那些存储有数据 但是已经被停止的线程
                // 然后将已停止线程的数据 移交到当前线程管理
                list = GetUnownedList();
                // 如果没有 那么就新建一个列表 然后更新尾指针的位置
                if (list == null)
                {
                    list = new ThreadLocalList(Thread.CurrentThread);
                    m_tailList.m_nextList = list;
                    m_tailList = list;
                }
            }
            m_locals.Value = list;
        }
    }
    else
    {
        return null;
    }
    Debug.Assert(list != null);
    return list;
}
/// <summary>
/// Adds an object to the <see cref="ConcurrentBag{T}"/>.
/// </summary>
/// <param name="item">The object to be added to the
/// <see cref="ConcurrentBag{T}"/>. The value can be a null reference
/// (Nothing in Visual Basic) for reference types.</param>
public void Add(T item)
{
    // 获取该线程的本地列表, 如果此线程不存在, 则创建一个新列表 (第一次调用 add)
    ThreadLocalList list = GetThreadList(true);
    // 实际的数据添加操作 在AddInternal中执行
    AddInternal(list, item);
}

/// <summary>
/// </summary>
/// <param name="list"></param>
/// <param name="item"></param>
private void AddInternal(ThreadLocalList list, T item)
{
    bool lockTaken = false;
    try
    {
        #pragma warning disable 0420
        Interlocked.Exchange(ref list.m_currentOp, (int)ListOperation.Add);
        #pragma warning restore 0420
        // 同步案例:
        // 如果列表计数小于两个, 因为是双向链表的关系 为了避免与任何窃取线程发生冲突 必须获取锁
        // 如果设置了 m_needSync, 这意味着有一个线程需要冻结包 也必须获取锁
        if (list.Count < 2 || m_needSync)
        {
            // 将其重置为None 以避免与窃取线程的死锁
            list.m_currentOp = (int)ListOperation.None;
            // 锁定当前对象
            Monitor.Enter(list, ref lockTaken);
        }
        // 调用 ThreadLocalList.Add方法 将数据添加到双向链表中
        // 如果已经锁定 那么说明线程安全  可以更新Count 计数
        list.Add(item, lockTaken);
    }
    finally
    {
        list.m_currentOp = (int)ListOperation.None;
        if (lockTaken)
        {
            Monitor.Exit(list);
        }
    }
}

从上边代码中,大家能够很明白的知情Add()措施是何等运维的,当中的主要性正是GetThreadList()办法,通过该办法能够取妥当前线程的多少存款和储蓄列表对象,要是不设有数量存款和储蓄列表,它会活动创立或许通过GetUnownedList()办法来探究这个被结束不过还蕴藏有数量列表的线程,然后将数据列表再次回到给当下线程中,防止了内部存款和储蓄器泄漏。

在数据增加的进度中,完毕了细颗粒度的lock同步锁,所以质量会超级高。删除和任何操作与新扩展相符,本文不再赘述。

由来事件下结论收尾,有不明之处还请指教。                2018-08-17   16:43:19

 

a.首先介绍下new 关键字的实施的时候会进行怎么样

  需求专一的是不管协变仍然逆变也只可以在泛型接口中来使用。


(其它还大概有事件的订阅者和事件源之间的订阅关系subscribe relationship)

 

  

  • 一、前言
  • 二、ConcurrentBag类
  • 三、 ConcurrentBag线程安全完毕原理
    • 1. ConcurrentBag的私人商品房字段
    • 2. 用于数据存款和储蓄的TrehadLocalList类
    • 3. ConcurrentBag完毕新添成分
    • 4. ConcurrentBag 如何贯彻迭代器情势
  • 四、总结
  • 小编水平有限,即便不当迎接各位顶牛指正!

本文由long8发布于long8,转载请注明出处:事件深入分析,托管堆在运维时的涉及

上一篇:面向对象之三大特征,基于DEV控件库的webservice打 下一篇:没有了
猜你喜欢
热门排行
精彩图文