[Day 7] .Net WhenAll 底层(2)

前言

我们今天要试着解决昨天阅读 WhenAll 留下来的两个问题

  1. 为何要 "atomic" 的把 task 的总数减一, 看起来只有一条 thread 会动到啊 ?
  2. 如果 WhenAll 在查看 task 状态时, task 未完成会进入另一条路线, 那条路线发生了什麽呢 ?

另一条路线

当 WhenAll 检测到尚未完成的任务时会进入 task.AddCompletionAction(this); 这里的 task 是待完成列表的其中一个任务, this 是运行 WhenAll 的本人。

跳过层层包装我们会看到

private void AddCompletionAction(ITaskCompletionAction action, bool addBeforeOthers)
{
    if (!AddTaskContinuation(action, addBeforeOthers))
        action.Invoke(this);  
}

AddTaskContinuation(action, addBeforeOthers) 为 false 表示任务已经完成, 在这里调用 WhenAll 中的 Invoke 把待完成任务总数减1。

接着我们了解一下 AddCompletionAction 里面发生甚麽事

private bool AddTaskContinuation(object tc, bool addBeforeOthers)
{
    Contract.Requires(tc != null);

    // 检测目前所在执行列表里的任务, 是否完成
    if (IsCompleted) return false;

    // 利用 CAS 把 tc ( WhenAllPromis 这个物件 ) 送入连续任务
    if ((m_continuationObject != null) || (Interlocked.CompareExchange(ref m_continuationObject, tc, null) != null))
    {
       // 当一般 CAS 方法失败後用这个一定成功的 CAS 方法。 不细讲
        return AddTaskContinuationComplex(tc, addBeforeOthers);
    }
    else return true; // 回传 true, 成功把 WhenAllPromise 加入到这个执行列表里的任务的连续任务区。
}

注 : WhenAllPromise 事当我们调用 WhenAll 所生成的物件, 上一篇文提到的事情都是在, WhenAllPromise 发生的

当 WhenAllPromise 这个物件被送入连续任务後发生了什麽呢 ?

这个疑问会在之後解答, 因为这个 method 就写到这里了。

总结

Task.WhenAll( taskList ) 调用後执行动作

taskList = 待完成任务列表, 我自己命名的变数

  1. 创建 WhenAllPromise 物件, 且传入 taskList
  2. 把 taskList 的长度(视为未完成任务数量)存在 WhenAllPromise 物件中
  3. WhenAllPromise 内部执行依序扫描 taskList 内的 task
  4. 当 task 已完成, 就把 未完成任务数量 减 1 ( 要 atomic , 因为 WhenAllPromise 有可能被挂载到别的 task 执行, 就会被视为 multi-thread )
  5. 当 task 未完成, 就把 WhenAllPromise 的 reference 挂载到 未完成的 task 的连续任务区(这个名词是我自己取的 XD )
  6. 当一次全部扫描结束, 若是 WhenAllPromise 内的未完成任务数量为0, 表示任务全部完成, 当即设置回传变数, 与进行回传。
  7. 有可能未完成任务数量没有归零, 表示 WhenAllPromise 有在上面的第 5 步挂载到别的 task。WhenAllPromise 不会结束。至於被挂载进连续任务区的任务会被如何处理, 会在 Day 10 跟大家解释清楚。

值得注意的是因为每个 task 的连续任务区都有可能在任何时刻, 被别的 thread 添加, 因此也被设计成一个 lock-free data structure.

另外, 未完成任务数量因为WhenAllPromise被挂载到别条 thread , 为了避免 race condition 也用了 atomic 变数。

明日进度

开始看看 task 的创建, 试着了解 task 的生命历程吧。

明天见!


<<:  Day07 - Docker 101 容器操作篇

>>:  鬼故事 - 这东西真烂

Day11 小实作-数字时钟

使用 React useState Hook 创建数字时钟 import React, {useSt...

生成模式 - abstract factory

首先从生成模式开始,第一种生成模式是 abstract factory (抽象工厂) 抽象工厂的目的...

番外篇(2)一起来做 To Do List!- 实作篇(1)

上一篇先介绍运用的知识点,这篇会着重在实作时的心路历程...不是啦,是怎麽把这个网页写出来的。先上成...

伸缩自如的Flask [day 22] pythonanywhere 部署

在我开始介绍Google Cloud Platform的Google app engine之前,我想...

Angular 路由守卫(登入篇)

经过了昨天的介绍,今天就来看看使用登入范例罗 今天的登入资料依然是使用 FakeStoreAPI 登...