C# Task的GetAwaiter和ConfigureAwait
个人感觉Task 的GetAwaiter和ConfigureAwait也是比较好理解的,首先看看他们的实现
public class Task<TResult> : Task
{
//Gets an awaiter used to await this
public new TaskAwaiter<TResult> GetAwaiter()
{
return new TaskAwaiter<TResult>(this);
} //Configures an awaiter used to await this
public new ConfiguredTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext)
{
return new ConfiguredTaskAwaitable<TResult>(this, continueOnCapturedContext);
}
}
现在我们来看看TaskAwaiter<TResult>和ConfiguredTaskAwaitable<TResult>的实现:
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion
{
private readonly Task<TResult> m_task;
internal TaskAwaiter(Task<TResult> task)
{
Contract.Requires(task != null, "Constructing an awaiter requires a task to await.");
m_task = task;
} public bool IsCompleted
{
get { return m_task.IsCompleted; }
} public void OnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:true);
} public void UnsafeOnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:false);
} public TResult GetResult()
{
TaskAwaiter.ValidateEnd(m_task);
return m_task.ResultOnSuccess;
}
} public struct ConfiguredTaskAwaitable<TResult>
{
private readonly ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter m_configuredTaskAwaiter;
internal ConfiguredTaskAwaitable(Task<TResult> task, bool continueOnCapturedContext)
{
m_configuredTaskAwaiter = new ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter(task, continueOnCapturedContext);
} public ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter GetAwaiter()
{
return m_configuredTaskAwaiter;
} [HostProtection(Synchronization = true, ExternalThreading = true)]
public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion
{
private readonly Task<TResult> m_task;
private readonly bool m_continueOnCapturedContext;
internal ConfiguredTaskAwaiter(Task<TResult> task, bool continueOnCapturedContext)
{
Contract.Requires(task != null, "Constructing an awaiter requires a task to await.");
m_task = task;
m_continueOnCapturedContext = continueOnCapturedContext;
} public bool IsCompleted
{
get { return m_task.IsCompleted; }
} public void OnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:true);
} public void UnsafeOnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:false);
} public TResult GetResult()
{
TaskAwaiter.ValidateEnd(m_task);
return m_task.ResultOnSuccess;
}
}
} public struct TaskAwaiter : ICriticalNotifyCompletion
{
private readonly Task m_task;
internal TaskAwaiter(Task task)
{
Contract.Requires(task != null, "Constructing an awaiter requires a task to await.");
m_task = task;
} public void OnCompleted(Action continuation)
{
OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:true);
} public void UnsafeOnCompleted(Action continuation)
{
OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:false);
} internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext)
{
if (continuation == null) throw new ArgumentNullException("continuation");
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; // If TaskWait* ETW events are enabled, trace a beginning event for this await
// and set up an ending event to be traced when the asynchronous await completes.
if ( TplEtwProvider.Log.IsEnabled() || Task.s_asyncDebuggingEnabled)
{
continuation = OutputWaitEtwEvents(task, continuation);
} // Set the continuation onto the awaited task.
task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext, ref stackMark);
} public void GetResult()
{
ValidateEnd(m_task);
} internal static void ValidateEnd(Task task)
{
if (task.IsWaitNotificationEnabledOrNotRanToCompletion)
{
HandleNonSuccessAndDebuggerNotification(task);
}
} /// Ensures the task is completed, triggers any necessary debugger breakpoints for completing
/// the await on the task, and throws an exception if the task did not complete successfully.
private static void HandleNonSuccessAndDebuggerNotification(Task task)
{
if (!task.IsCompleted)
{
bool taskCompleted = task.InternalWait(Timeout.Infinite, default(CancellationToken));
Contract.Assert(taskCompleted, "With an infinite timeout, the task should have always completed.");
} // Now that we're done, alert the debugger if so requested
task.NotifyDebuggerOfWaitCompletionIfNecessary(); // And throw an exception if the task is faulted or canceled.
if (!task.IsRanToCompletion) ThrowForNonSuccess(task);
}
}
TaskAwaiter<TResult>中的OnCompleted和UnsafeOnCompleted方法 参数continueOnCapturedContext为true,GetResult主要是调用TaskAwaiter.ValidateEnd(m_task)方法,而ConfiguredTaskAwaiter的OnCompleted和UnsafeOnCompleted方法中的m_continueOnCapturedContext参数不一定是true,是外面调用task的ConfigureAwait的参数continueOnCapturedContext,ConfiguredTaskAwaiter的GetResult也是调用TaskAwaiter.ValidateEnd(m_task)方法。那么让我们来看看TaskAwaiter的ValidateEnd方法,同时TaskAwaiter的GetResult方法也是调用自己的ValidateEnd,ValidateEnd方法主要是调用HandleNonSuccessAndDebuggerNotification方法,在HandleNonSuccessAndDebuggerNotification方法中检查task是否完成,没有完成我们就调用 task.InternalWait(Timeout.Infinite, default(CancellationToken))来阻塞task直到完成。
Task的ConfigureAwait中参数continueOnCapturedContext最后传递到了TaskAwaiter的OnCompletedInternal方法,也就是说你在调用Awaiter 的OnCompleted和UnsafeOnCompleted方法才有区别,一般调用GetResult是没有区别的。,OnCompletedInternal方法主要是调用task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext, ref stackMark);方法,task的SetContinuationForAwait实现如下:
public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
{
internal void SetContinuationForAwait(Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext, ref StackCrawlMark stackMark)
{
Contract.Requires(continuationAction != null);
TaskContinuation tc = null; // If the user wants the continuation to run on the current "context" if there is one...
if (continueOnCapturedContext)
{
var syncCtx = SynchronizationContext.CurrentNoFlow;
if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
{
tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext, ref stackMark);
}
else
{
var scheduler = TaskScheduler.InternalCurrent;
if (scheduler != null && scheduler != TaskScheduler.Default)
{
tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext, ref stackMark);
}
}
} if (tc == null && flowExecutionContext)
{
tc = new AwaitTaskContinuation(continuationAction, flowExecutionContext: true, stackMark: ref stackMark);
} if (tc != null)
{
if (!AddTaskContinuation(tc, addBeforeOthers: false))
tc.Run(this, bCanInlineContinuationTask: false);
}
else
{
Contract.Assert(!flowExecutionContext, "We already determined we're not required to flow context.");
if (!AddTaskContinuation(continuationAction, addBeforeOthers: false))
AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
}
}
}
Task的SetContinuationForAwait方法里面涉及到SynchronizationContextAwaitTaskContinuation,TaskSchedulerAwaitTaskContinuation和AwaitTaskContinuation类,他们的主要方法如下,这里我也没有去调试,:
/// Task continuation for awaiting with a current synchronization context.
internal sealed class SynchronizationContextAwaitTaskContinuation : AwaitTaskContinuation
{
private readonly static SendOrPostCallback s_postCallback = state => ((Action)state)(); // can't use InvokeAction as it's SecurityCritical
private static ContextCallback s_postActionCallback;
private readonly SynchronizationContext m_syncContext; internal SynchronizationContextAwaitTaskContinuation(SynchronizationContext context, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) :base(action, flowExecutionContext, ref stackMark)
{
Contract.Assert(context != null);
m_syncContext = context;
} internal sealed override void Run(Task task, bool canInlineContinuationTask)
{
if (canInlineContinuationTask && m_syncContext == SynchronizationContext.CurrentNoFlow)
{
RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask);
}
else
{
TplEtwProvider etwLog = TplEtwProvider.Log;
if (etwLog.IsEnabled())
{
m_continuationId = Task.NewId();
etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
}
RunCallback(GetPostActionCallback(), this, ref Task.t_currentTask);
}
} private static void PostAction(object state)
{
var c = (SynchronizationContextAwaitTaskContinuation)state; TplEtwProvider etwLog = TplEtwProvider.Log;
if (etwLog.TasksSetActivityIds && c.m_continuationId != )
{
c.m_syncContext.Post(s_postCallback, GetActionLogDelegate(c.m_continuationId, c.m_action));
}
else
{
c.m_syncContext.Post(s_postCallback, c.m_action); // s_postCallback is manually cached, as the compiler won't in a SecurityCritical method
}
} private static ContextCallback GetPostActionCallback()
{
ContextCallback callback = s_postActionCallback;
if (callback == null) { s_postActionCallback = callback = PostAction; } // lazily initialize SecurityCritical delegate
return callback;
}
} internal sealed class TaskSchedulerAwaitTaskContinuation : AwaitTaskContinuation
{
private readonly TaskScheduler m_scheduler; internal TaskSchedulerAwaitTaskContinuation(TaskScheduler scheduler, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) : base(action, flowExecutionContext, ref stackMark)
{
Contract.Assert(scheduler != null);
m_scheduler = scheduler;
} internal sealed override void Run(Task ignored, bool canInlineContinuationTask)
{
// If we're targeting the default scheduler, we can use the faster path provided by the base class.
if (m_scheduler == TaskScheduler.Default)
{
base.Run(ignored, canInlineContinuationTask);
}
else
{ bool inlineIfPossible = canInlineContinuationTask &&
(TaskScheduler.InternalCurrent == m_scheduler || Thread.CurrentThread.IsThreadPoolThread); var task = CreateTask(state => {
try { ((Action)state)(); }
catch (Exception exc) { ThrowAsyncIfNecessary(exc); }
}, m_action, m_scheduler); if (inlineIfPossible)
{
InlineIfPossibleOrElseQueue(task, needsProtection: false);
}
else
{
try { task.ScheduleAndStart(needsProtection: false); }
catch (TaskSchedulerException) { } // No further action is necessary, as ScheduleAndStart already transitioned task to faulted
}
}
}
}
internal class AwaitTaskContinuation : TaskContinuation, IThreadPoolWorkItem
{
private readonly ExecutionContext m_capturedContext;
protected readonly Action m_action;
protected int m_continuationId; internal AwaitTaskContinuation(Action action, bool flowExecutionContext, ref StackCrawlMark stackMark)
{
Contract.Requires(action != null);
m_action = action;
if (flowExecutionContext)
{
m_capturedContext = ExecutionContext.Capture(ref stackMark, ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
}
} internal AwaitTaskContinuation(Action action, bool flowExecutionContext)
{
Contract.Requires(action != null);
m_action = action;
if (flowExecutionContext)
{
m_capturedContext = ExecutionContext.FastCapture();
}
} protected Task CreateTask(Action<object> action, object state, TaskScheduler scheduler)
{
Contract.Requires(action != null);
Contract.Requires(scheduler != null); return new Task( action, state, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler)
{
CapturedContext = m_capturedContext
};
} internal override void Run(Task task, bool canInlineContinuationTask)
{ if (canInlineContinuationTask && IsValidLocationForInlining)
{
RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask); // any exceptions from m_action will be handled by s_callbackRunAction
}
else
{
TplEtwProvider etwLog = TplEtwProvider.Log;
if (etwLog.IsEnabled())
{
m_continuationId = Task.NewId();
etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
}
ThreadPool.UnsafeQueueCustomWorkItem(this, forceGlobal: false);
}
} [SecurityCritical]
void ExecuteWorkItemHelper()
{
var etwLog = TplEtwProvider.Log;
Guid savedActivityId = Guid.Empty;
if (etwLog.TasksSetActivityIds && m_continuationId != )
{
Guid activityId = TplEtwProvider.CreateGuidForTaskID(m_continuationId);
System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
}
try
{
if (m_capturedContext == null)
{
m_action();
}
else
{
try
{
ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action, true);
}
finally { m_capturedContext.Dispose(); }
}
}
finally
{
if (etwLog.TasksSetActivityIds && m_continuationId != )
{
System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId);
}
}
}
void IThreadPoolWorkItem.ExecuteWorkItem()
{
// inline the fast path
if (m_capturedContext == null && !TplEtwProvider.Log.IsEnabled())
{
m_action();
}
else
{
ExecuteWorkItemHelper();
}
} private static ContextCallback s_invokeActionCallback; private static void InvokeAction(object state) { ((Action)state)(); } protected static ContextCallback GetInvokeActionCallback()
{
ContextCallback callback = s_invokeActionCallback;
if (callback == null) { s_invokeActionCallback = callback = InvokeAction; } // lazily initialize SecurityCritical delegate
return callback;
} protected void RunCallback(ContextCallback callback, object state, ref Task currentTask)
{
Contract.Requires(callback != null);
Contract.Assert(currentTask == Task.t_currentTask); var prevCurrentTask = currentTask;
try
{
if (prevCurrentTask != null) currentTask = null;
if (m_capturedContext == null) callback(state);
else ExecutionContext.Run(m_capturedContext, callback, state, true);
}
catch (Exception exc) // we explicitly do not request handling of dangerous exceptions like AVs
{
ThrowAsyncIfNecessary(exc);
}
finally
{
if (prevCurrentTask != null) currentTask = prevCurrentTask;
if (m_capturedContext != null) m_capturedContext.Dispose();
}
} }
我在调试的时候,SetContinuationForAwait方法中的TaskContinuation是AwaitTaskContinuation实例,在AwaitTaskContinuation构造方法中【 m_capturedContext = ExecutionContext.FastCapture();】来捕获上下文。最后在Task的SetContinuationForAwait调用AwaitTaskContinuation的Run方法【tc.Run(this, bCanInlineContinuationTask: false)】,AwaitTaskContinuation的Run实现非常简单, 调用 ThreadPool.UnsafeQueueCustomWorkItem(this, forceGlobal: false);,实际就是调用AwaitTaskContinuation的ExecuteWorkItem方法,检查上下文m_capturedContext是否存在,存在调用ExecuteWorkItemHelper,ExecuteWorkItemHelper里面再调用 ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action, true); AwaitTaskContinuation是不是比较简单。SynchronizationContextAwaitTaskContinuation和TaskSchedulerAwaitTaskContinuation的Run方法就忽略吧,更简单。
C# Task的GetAwaiter和ConfigureAwait的更多相关文章
- 任务(task)
任务概述 线程(Thread)是创建并发的底层工具,因此有一定的局限性(不易得到返回值(必须通过创建共享域):异常的捕获和处理也麻烦:同时线程执行完毕后无法再次开启该线程),这些局限性会降低性能同时影 ...
- C#中 Thread,Task,Async/Await 异步编程
什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调 ...
- 15.5.3 【Task实现细节】状态机的结构
状态机的整体结构非常简单.它总是使用显式接口实现,以实现.NET 4.5引入的 IAsync StateMachine 接口,并且只包含该接口声明的两个方法,即 MoveNext 和 SetState ...
- 15.5.2 【Task实现细节】骨架方法的结构
尽管骨架方法中的代码非常简单,但它暗示了状态机的职责.代码清单15-11生成的骨架方 法如下所示: [DebuggerStepThrough] [AsyncStateMachine(typeof(De ...
- 15.3 Task 语法和语义
15.3.1 声明异步方法和返回类型 async static void GetStringAsync() { using (var client = new HttpClient()) { Task ...
- C# 使用Task执行异步操作
为什么要使用 Task Task 和 Thread 区别 Task 介绍 Task 简单实现 Task 执行状态 为什么要使用 Task 线程是创建并发的底层工具,因此具有一定的局限性. 没有简单的方 ...
- C# Task.WhenAll
1.有时候我们需要同时执行一些操作,然后把这些操作的结果进行汇总,以达到用异步处理降低操作耗时的效果,此时我们会考虑使用Task,而Task.WhenAll则排上了用场. public void Is ...
- C#关于在返回值为Task方法中使用Thread.Sleep引发的思考
起因 最近有个小伙伴提出了一个问题,就是在使用.net core的BackgroundService的时候,对应的ExecuteAsync方法里面写如下代码,会使程序一直卡在当前方法,不会继续执行,代 ...
- 深入探究 WinRT 和 await
在最近发布的使用 Windows 运行时中异步性来始终保持应用程序能够快速流畅地运行这篇博文中,包含了一些如何在 C# 和 Visual Basic 中使用 await 关键字的示例,允许开发人员在使 ...
随机推荐
- Codechef FIBTREE 树链剖分 主席树 LCA 二次剩余 快速幂
原文链接https://www.cnblogs.com/zhouzhendong/p/CC-FIBTREE.html 题目传送门 - CC-FIBTREE 题意 给定一个有 $n$ 个节点,初始点权都 ...
- UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表
原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html 题目传送门 - UOJ#219 (推荐,题面清晰) 题目传送门 - BZOJ4650 题意 ...
- HDFS-HA高可用 | Yarn-HA
HDFS-HA HA(High Available),即高可用(7*24小时不中断服务) 单点故障即有一台机器挂了导致全部都挂了:HA就是解决单点故障,就是针对NameNode: 主Active:读写 ...
- ASP.NET 页面执行顺序
1.对象初始化(Onlnit方法) 页面中的控件(包括页面本身)都是在它们最初的form中被首次初始化的,通过在aspx页面的后台代码文件的构造器中声明你的对象,页面将知道对象的类型,并知道需要创建多 ...
- Flume的概述和安装部署
一.Flume概述 Flume是一种分布式.可靠且可用的服务,用于有效的收集.聚合和移动大量日志文件数据.Flume具有基于流数据流的简单灵活的框架,具有可靠的可靠性机制和许多故障转移和恢复机制,具有 ...
- 【JavaScript】underscore
例: 'use strict'; _.map([1, 2, 3], (x) => x * x); // [1, 4, 9] No1: [every/some] 当集合的所有元素都满足条件时,_. ...
- Booksort POJ - 3460 (IDA*)
Description The Leiden University Library has millions of books. When a student wants to borrow a ce ...
- type__元组、字典、集合
- VirtualBox查看虚拟机IP地址
在终端输入如下内容 ifconfig 结果如图所示 eth0 内容中 inet 后的地址10.0.2.15即为虚拟机IP地址,lo 中的 inet 后的地址时本地环回,用于测试网络
- 在Qt中调用Mupdf库进行pdf显示
2018.5.10 更新内存对齐说明 感谢知乎网友@孤独子狮 指出QImage处需要考虑内存对齐的问题.因为本人缺乏跨平台.图形库开发经验,所以在调试成功后就没有深入探究. 主要修改了QImage的构 ...