看了上一篇C# Task 是什么?返回值如何实现? Wait如何实现 我们提到FinishContinuations方法中会调用TaskContinuation实例,那么我们的ContinueWith就应该非常简单,只需要把TASK放到TaskContinuation结合中就可以了,ContinueWith可以是 Action<Task<TResult>>也可以是 Func<Task<TResult>,TNewResult> ,其中Task<TResult>的实现如下:

  1. public class Task<TResult> : Task{
  2. //Creates a continuation that executes when the target Task{TResult}" completes
  3. public Task ContinueWith(Action<Task<TResult>> continuationAction)
  4. {
  5. StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  6. return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
  7. }
  8. internal Task ContinueWith(Action<Task<TResult>> continuationAction, TaskScheduler scheduler, CancellationToken cancellationToken,TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
  9. {
  10. if (continuationAction == null)
  11. {
  12. throw new ArgumentNullException("continuationAction");
  13. }
  14. if (scheduler == null)
  15. {
  16. throw new ArgumentNullException("scheduler");
  17. }
  18. TaskCreationOptions creationOptions;
  19. InternalTaskOptions internalOptions;
  20. CreationOptionsFromContinuationOptions(continuationOptions,out creationOptions,out internalOptions);
  21.  
  22. Task continuationTask = new ContinuationTaskFromResultTask<TResult>(this, continuationAction, null, creationOptions, internalOptions,ref stackMark);
  23. ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
  24. return continuationTask;
  25. }
  26.  
  27. public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state)
  28. {
  29. StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  30. return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
  31. }
  32.  
  33. // Same as the above overload, just with a stack mark.
  34. internal Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state,TaskScheduler scheduler, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
  35. {
  36. if (continuationFunction == null)
  37. {
  38. throw new ArgumentNullException("continuationFunction");
  39. }
  40. if (scheduler == null)
  41. {
  42. throw new ArgumentNullException("scheduler");
  43. }
  44.  
  45. TaskCreationOptions creationOptions;
  46. InternalTaskOptions internalOptions;
  47. CreationOptionsFromContinuationOptions(continuationOptions,out creationOptions,out internalOptions);
  48.  
  49. Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>(this, continuationFunction, state,creationOptions, internalOptions,ref stackMark);
  50. ContinueWithCore(continuationFuture, scheduler, cancellationToken, continuationOptions);
  51. return continuationFuture;
  52. }
  53. }

ContinueWith的核心是调用Task的ContinueWithCore方法,这里把我们的Action或Fun包装成子的Task,比如这里的ContinuationResultTaskFromResultTask实现【很是标准】如下:

  1. internal sealed class ContinuationResultTaskFromResultTask<TAntecedentResult, TResult> : Task<TResult>
  2. {
  3. private Task<TAntecedentResult> m_antecedent;
  4. public ContinuationResultTaskFromResultTask(
  5. Task<TAntecedentResult> antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
  6. base(function, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
  7. {
  8. Contract.Requires(function is Func<Task<TAntecedentResult>, TResult> || function is Func<Task<TAntecedentResult>, object, TResult>, "Invalid delegate type in ContinuationResultTaskFromResultTask");
  9. m_antecedent = antecedent;
  10. PossiblyCaptureContext(ref stackMark);
  11. }
  12.  
  13. internal override void InnerInvoke()
  14. {
  15. var antecedent = m_antecedent;
  16. Contract.Assert(antecedent != null, "No antecedent was set for the ContinuationResultTaskFromResultTask.");
  17. m_antecedent = null;
  18. antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
  19.  
  20. // Invoke the delegate
  21. Contract.Assert(m_action != null);
  22. var func = m_action as Func<Task<TAntecedentResult>, TResult>;
  23. if (func != null)
  24. {
  25. m_result = func(antecedent);
  26. return;
  27. }
  28. var funcWithState = m_action as Func<Task<TAntecedentResult>, object, TResult>;
  29. if (funcWithState != null)
  30. {
  31. m_result = funcWithState(antecedent, m_stateObject);
  32. return;
  33. }
  34. Contract.Assert(false, "Invalid m_action in ContinuationResultTaskFromResultTask");
  35. }
  36. }

ContinuationResultTaskFromResultTask<TAntecedentResult, TResult> 就重写基类Task的InnerInvoke方法,现在回到Task的ContinueWithCore方法:

  1. public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
  2. {
  3. /// Registers the continuation and possibly runs it (if the task is already finished).
  4. internal void ContinueWithCore(Task continuationTask, TaskScheduler scheduler,CancellationToken cancellationToken, TaskContinuationOptions options)
  5. {
  6. Contract.Requires(continuationTask != null, "Task.ContinueWithCore(): null continuationTask");
  7. Contract.Requires(scheduler != null, "Task.ContinueWithCore(): null scheduler");
  8. Contract.Requires(!continuationTask.IsCompleted, "Did not expect continuationTask to be completed");
  9.  
  10. // Create a TaskContinuation
  11. TaskContinuation continuation = new StandardTaskContinuation(continuationTask, options, scheduler);
  12.  
  13. // If cancellationToken is cancellable, then assign it.
  14. if (cancellationToken.CanBeCanceled)
  15. {
  16. if (IsCompleted || cancellationToken.IsCancellationRequested)
  17. {
  18. continuationTask.AssignCancellationToken(cancellationToken, null, null);
  19. }
  20. else
  21. {
  22. continuationTask.AssignCancellationToken(cancellationToken, this, continuation);
  23. }
  24. }
  25.  
  26. // In the case of a pre-canceled token, continuationTask will have been completed
  27. // in a Canceled state by now. If such is the case, there is no need to go through
  28. // the motions of queuing up the continuation for eventual execution.
  29. if (!continuationTask.IsCompleted)
  30. {
  31. if ((this.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != && !(this is ITaskCompletionAction))
  32. {
  33. var etwLog = TplEtwProvider.Log;
  34. if (etwLog.IsEnabled())
  35. {
  36. etwLog.AwaitTaskContinuationScheduled(TaskScheduler.Current.Id, Task.CurrentId ?? , continuationTask.Id);
  37. }
  38. }
  39.  
  40. // Attempt to enqueue the continuation
  41. bool continuationQueued = AddTaskContinuation(continuation, addBeforeOthers: false);
  42.  
  43. // If the continuation was not queued (because the task completed), then run it now.
  44. if (!continuationQueued) continuation.Run(this, bCanInlineContinuationTask: true);
  45. }
  46. }
  47.  
  48. private bool AddTaskContinuation(object tc, bool addBeforeOthers)
  49. {
  50. Contract.Requires(tc != null);
  51. if (IsCompleted) return false;
  52.  
  53. // Try to just jam tc into m_continuationObject
  54. if ((m_continuationObject != null) || (Interlocked.CompareExchange(ref m_continuationObject, tc, null) != null))
  55. {
  56. return AddTaskContinuationComplex(tc, addBeforeOthers);
  57. }
  58. else return true;
  59. }
  60.  
  61. private bool AddTaskContinuationComplex(object tc, bool addBeforeOthers)
  62. {
  63. Contract.Requires(tc != null, "Expected non-null tc object in AddTaskContinuationComplex");
  64. object oldValue = m_continuationObject;
  65. // Logic for the case where we were previously storing a single continuation
  66. if ((oldValue != s_taskCompletionSentinel) && (!(oldValue is List<object>)))
  67. {
  68. List<object> newList = new List<object>();
  69. newList.Add(oldValue);
  70. Interlocked.CompareExchange(ref m_continuationObject, newList, oldValue);
  71. }
  72.  
  73. // m_continuationObject is guaranteed at this point to be either a List or
  74. // s_taskCompletionSentinel.
  75. List<object> list = m_continuationObject as List<object>;
  76. Contract.Assert((list != null) || (m_continuationObject == s_taskCompletionSentinel),"Expected m_continuationObject to be list or sentinel");
  77. if (list != null)
  78. {
  79. lock (list)
  80. {
  81. if (m_continuationObject != s_taskCompletionSentinel)
  82. {
  83. // Before growing the list we remove possible null entries that are the
  84. // result from RemoveContinuations()
  85. if (list.Count == list.Capacity)
  86. {
  87. list.RemoveAll(s_IsTaskContinuationNullPredicate);
  88. }
  89.  
  90. if (addBeforeOthers)
  91. list.Insert(0, tc);
  92. else
  93. list.Add(tc);
  94.  
  95. return true; // continuation successfully queued, so return true.
  96. }
  97. }
  98. }
  99. // We didn't succeed in queuing the continuation, so return false.
  100. return false;
  101. }
  102.  
  103. /// Handles everything needed for associating a CancellationToken with a task which is being constructed.
  104. /// This method is meant to be be called either from the TaskConstructorCore or from ContinueWithCore
  105. private void AssignCancellationToken(CancellationToken cancellationToken, Task antecedent, TaskContinuation continuation)
  106. {
  107. ContingentProperties props = EnsureContingentPropertiesInitialized(needsProtection: false);
  108. props.m_cancellationToken = cancellationToken;
  109.  
  110. try
  111. {
  112. if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
  113. {
  114. cancellationToken.ThrowIfSourceDisposed();
  115. }
  116. if ((((InternalTaskOptions)Options & (InternalTaskOptions.QueuedByRuntime | InternalTaskOptions.PromiseTask | InternalTaskOptions.LazyCancellation)) == ))
  117. {
  118. if (cancellationToken.IsCancellationRequested)
  119. {
  120. // Fast path for an already-canceled cancellationToken
  121. this.InternalCancel(false);
  122. }
  123. else
  124. {
  125. // Regular path for an uncanceled cancellationToken
  126. CancellationTokenRegistration ctr;
  127. if (antecedent == null)
  128. {
  129. ctr = cancellationToken.InternalRegisterWithoutEC(s_taskCancelCallback, this);
  130. }
  131. else
  132. {
  133. ctr = cancellationToken.InternalRegisterWithoutEC(s_taskCancelCallback,new Tuple<Task, Task, TaskContinuation>(this, antecedent, continuation));
  134. }
  135. props.m_cancellationRegistration = new Shared<CancellationTokenRegistration>(ctr);
  136. }
  137. }
  138. }
  139. catch
  140. {
  141. if ((m_parent != null) &&((Options & TaskCreationOptions.AttachedToParent) != )&& ((m_parent.Options & TaskCreationOptions.DenyChildAttach) == ))
  142. {
  143. m_parent.DisregardChild();
  144. }
  145. throw;
  146. }
  147. }
  148.  
  149. private readonly static Action<Object> s_taskCancelCallback = new Action<Object>(TaskCancelCallback);
  150. private static void TaskCancelCallback(Object o)
  151. {
  152. var targetTask = o as Task;
  153. if (targetTask == null)
  154. {
  155. var tuple = o as Tuple<Task, Task, TaskContinuation>;
  156. if (tuple != null)
  157. {
  158. targetTask = tuple.Item1;
  159.  
  160. Task antecedentTask = tuple.Item2;
  161. TaskContinuation continuation = tuple.Item3;
  162. antecedentTask.RemoveContinuation(continuation);
  163. }
  164. }
  165. Contract.Assert(targetTask != null,"targetTask should have been non-null, with the supplied argument being a task or a tuple containing one");
  166. targetTask.InternalCancel(false);
  167. }
  168. }

ContinueWithCore实现也比较简单,首先把当前的continuationTask转换为StandardTaskContinuation,然后把CancellationToken赋给continuationTask,如果continuationTask没有完成, 那么调用AddTaskContinuation把continuationTask加到等待对象中,如果AddTaskContinuation添加失败,就直接调用continuationTask。 让我妈来看看StandardTaskContinuation的实现:

  1. internal abstract class TaskContinuation
  2. {
  3. internal abstract void Run(Task completedTask, bool bCanInlineContinuationTask);
  4.  
  5. /// <summary>Tries to run the task on the current thread, if possible; otherwise, schedules it.</summary>
  6. protected static void InlineIfPossibleOrElseQueue(Task task, bool needsProtection)
  7. {
  8. Contract.Requires(task != null);
  9. Contract.Assert(task.m_taskScheduler != null);
  10. if (needsProtection)
  11. {
  12. if (!task.MarkStarted())
  13. return; // task has been previously started or canceled. Stop processing.
  14. }
  15. else
  16. {
  17. task.m_stateFlags |= Task.TASK_STATE_STARTED;
  18. }
  19.  
  20. // Try to inline it but queue if we can't
  21. try
  22. {
  23. if (!task.m_taskScheduler.TryRunInline(task, taskWasPreviouslyQueued: false))
  24. {
  25. task.m_taskScheduler.InternalQueueTask(task);
  26. }
  27. }
  28. catch (Exception e)
  29. {
  30.  
  31. if (!(e is ThreadAbortException && (task.m_stateFlags & Task.TASK_STATE_THREAD_WAS_ABORTED) != )) // this ensures TAEs from QueueTask will be wrapped in TSE
  32. {
  33. TaskSchedulerException tse = new TaskSchedulerException(e);
  34. task.AddException(tse);
  35. task.Finish(false);
  36. }
  37. }
  38. }
  39. internal abstract Delegate[] GetDelegateContinuationsForDebugger();
  40. }
  41.  
  42. /// <summary>Provides the standard implementation of a task continuation.</summary>
  43. internal class StandardTaskContinuation : TaskContinuation
  44. {
  45. internal readonly Task m_task;
  46. internal readonly TaskContinuationOptions m_options;
  47. private readonly TaskScheduler m_taskScheduler;
  48. internal StandardTaskContinuation(Task task, TaskContinuationOptions options, TaskScheduler scheduler)
  49. {
  50. Contract.Requires(task != null, "TaskContinuation ctor: task is null");
  51. Contract.Requires(scheduler != null, "TaskContinuation ctor: scheduler is null");
  52. m_task = task;
  53. m_options = options;
  54. m_taskScheduler = scheduler;
  55. if (AsyncCausalityTracer.LoggingOn)
  56. AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, m_task.Id, "Task.ContinueWith: " + ((Delegate)task.m_action).Method.Name, );
  57.  
  58. if (Task.s_asyncDebuggingEnabled)
  59. {
  60. Task.AddToActiveTasks(m_task);
  61. }
  62. }
  63.  
  64. /// <summary>Invokes the continuation for the target completion task.</summary>
  65. /// <param name="completedTask">The completed task.</param>
  66. /// <param name="bCanInlineContinuationTask">Whether the continuation can be inlined.</param>
  67. internal override void Run(Task completedTask, bool bCanInlineContinuationTask)
  68. {
  69. Contract.Assert(completedTask != null);
  70. Contract.Assert(completedTask.IsCompleted, "ContinuationTask.Run(): completedTask not completed");
  71.  
  72. // Check if the completion status of the task works with the desired
  73. // activation criteria of the TaskContinuationOptions.
  74. TaskContinuationOptions options = m_options;
  75. bool isRightKind =
  76. completedTask.IsRanToCompletion ?
  77. (options & TaskContinuationOptions.NotOnRanToCompletion) == :
  78. (completedTask.IsCanceled ?
  79. (options & TaskContinuationOptions.NotOnCanceled) == :
  80. (options & TaskContinuationOptions.NotOnFaulted) == );
  81.  
  82. // If the completion status is allowed, run the continuation.
  83. Task continuationTask = m_task;
  84. if (isRightKind)
  85. {
  86. if (!continuationTask.IsCanceled && AsyncCausalityTracer.LoggingOn)
  87. {
  88. // Log now that we are sure that this continuation is being ran
  89. AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, continuationTask.Id, CausalityRelation.AssignDelegate);
  90. }
  91. continuationTask.m_taskScheduler = m_taskScheduler;
  92.  
  93. if (bCanInlineContinuationTask && // inlining is allowed by the caller
  94. (options & TaskContinuationOptions.ExecuteSynchronously) != ) // synchronous execution was requested by the continuation's creator
  95. {
  96. InlineIfPossibleOrElseQueue(continuationTask, needsProtection: true);
  97. }
  98. else
  99. {
  100. try { continuationTask.ScheduleAndStart(needsProtection: true); }
  101. catch (TaskSchedulerException)
  102. {
  103. // No further action is necessary -- ScheduleAndStart() already transitioned the
  104. // task to faulted. But we want to make sure that no exception is thrown from here.
  105. }
  106. }
  107. }
  108. // Otherwise, the final state of this task does not match the desired
  109. // continuation activation criteria; cancel it to denote this.
  110. else continuationTask.InternalCancel(false);
  111. }
  112. internal override Delegate[] GetDelegateContinuationsForDebugger()
  113. {
  114. if (m_task.m_action == null)
  115. {
  116. return m_task.GetDelegateContinuationsForDebugger();
  117. }
  118. return new Delegate[] { m_task.m_action as Delegate };
  119. }
  120. }

StandardTaskContinuation的实现非常简单,而Task的AssignCancellationToken方法也没什么可以说的,只是需要注意下一下回调s_taskCancelCallback。Task的AddTaskContinuation方法首先检查当前Task是否结束,结束了就不用再调用AddTaskContinuationComplex方法了,直接调用continuation.Run方法,AddTaskContinuationComplex方法会把task添加到m_continuationObject中,最后FinishContinuations在调用m_continuationObject中的TaskContinuation.Run方法。

总结一下:ContinueWith方法主要调用ContinueWithCore方法,ContinueWithCore方法主要是调用AddTaskContinuation,AddTaskContinuation方法把Task加到m_continuationObject,【如果主的Task已经完成,那么这里AddTaskContinuation返回false,则直接调用TaskContinuation.Run】,当主的Task完成时会调用FinishContinuations方法,FinishContinuations方法会检测m_continuationObject中TaskContinuation对象,一次调用它们的Run方法。

C# Task ContinueWith的实现的更多相关文章

  1. Web Api 图片上传,在使用 Task.ContinueWith 变量无法赋值问题

    细谈 Web Api 图片上传,在使用 Task.ContinueWith 变量无法赋值问题的解决办法!   在使用Asp.Net Web Api 图片上传接口的时候,到网上找了一些个例子,但大多数找 ...

  2. Task ContinueWith

    前正无生意,且记Task.ContinueWith之用法. using System; using System.Collections.Generic; using System.Diagnosti ...

  3. 细谈 Web Api 图片上传,在使用 Task.ContinueWith 变量无法赋值问题的解决办法!

    在使用Asp.Net Web Api 图片上传接口的时候,到网上找了一些个例子,但大多数找到都是这个版本! [HttpPost] public Task<Hashtable> ImgUpl ...

  4. C# Task ContinueWith

    static void Main(string[] args) { Task firstTask = Task.Run(() => { PrintPlus(); }); Task secondT ...

  5. 2、Task 使用 ContinueWith 而不要使用 Wait

    1.线程自旋:在阻塞线程的时候为了等待解锁(访问临界资源)(Sleep). 2.上下文切换:将处理器当前线程的状态保存到操作系统内部的线程对象中,然后再挑出一个就绪的线程,把上下文信息传递给处理器,然 ...

  6. Task类学习教程—组合任务ContinueWith

    Task类学习教程-组合任务.ContinueWith 一.简介 通过任务,可以指定在任务完成之后,应开始运行之后另一个特定任务.ContinueWith是Task根据其自身状况,决定后续应该作何操作 ...

  7. .Net多线程编程—任务Task

    1 System.Threading.Tasks.Task简介 一个Task表示一个异步操作,Task的创建和执行是独立的. 只读属性: 返回值 名称 说明 object AsyncState 表示在 ...

  8. 多线程之任务: Task 基础, 多任务并行执行, 并行运算(Parallel)

    Task - 基于线程池的任务(在 System.Threading.Tasks 命名空间下) 多 Task 的并行执行 Parallel - 并行计算(在 System.Threading.Task ...

  9. c# .Net并行和多线程编程之Task学习记录!

    任务Task和线程Thread的区别: 1.任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2.任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线 ...

随机推荐

  1. HTML中鼠标滚轮事件onmousewheel

    IE/Opera属于同一类型,使用attachEvent即可添加滚轮事件. /*IE注册事件*/ if(document.attachEvent){ document.attachEvent('onm ...

  2. 使用aws中国的s3时,制订bucket poicy时注意注意……

    { "Version": "2012-10-17", "Statement": [ { "Sid": "Pub ...

  3. 反向传播算法(前向传播、反向传播、链式求导、引入delta)

    参考链接: 一文搞懂反向传播算法

  4. os2

    1. os.getcwd() 显示当前路径 2. a = os.name 显示当前操作系统 3. a = listdir(path) 显示该路径的所有内容,类似与ls 4. os.chdir(&quo ...

  5. Constructing Roads-最小生成树(kruskal)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1102 题目描述: #include<cstdio> #include<cstring ...

  6. Booksort POJ - 3460 (IDA*)

    Description The Leiden University Library has millions of books. When a student wants to borrow a ce ...

  7. 错误: 在类中找不到 main 方法, 请将 main 方法定义为:public static void main(String[] args)否则 JavaFX 应用程序类必须扩展javafx.ap

    最近在使用eclipse编写java程序时遇到这样一个问题: 错误在类中找不到main方法,请将main方法定义为 public static void main(String[] args)否则 J ...

  8. 002.Kickstart部署之NFS架构

    一 准备 1.1 完整架构:Kickstart+DHCP+NFS+TFTP+PXE 1.2 组件应用 Kickstart服务端IP:172.24.8.12 DHCP:提供客户端IP,网关,镜像路径等: ...

  9. [OC] 杂项

    使用JSONModel的一个好处是,可以防止 [数据是NSNULL的时候,OC无法直接通过if(XX)来判空 ]引起的错误. 字符串与字符串对比不要使用 str1 != str2 这种写法,而用 ![ ...

  10. 在Node.js使用Promise的方式操作Mysql

    最近在学习Node.js,虽然早就听说了回调地狱结果过了一周就遇到了.所以花时间学习了了一下Promise.虽然还有Async/await.co.生成器等选择,但是因为本人基础较差,以及时间问题所以决 ...