理解TaskScheduler.Current的行为 [英] Understanding the behavior of TaskScheduler.Current
问题描述
下面是一个简单的WinForms应用程序:
Here's a simple WinForms app:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
var ts = TaskScheduler.FromCurrentSynchronizationContext();
await Task.Factory.StartNew(async () =>
{
Debug.WriteLine(new
{
where = "1) before await",
currentTs = TaskScheduler.Current,
thread = Thread.CurrentThread.ManagedThreadId,
context = SynchronizationContext.Current
});
await Task.Yield(); // or await Task.Delay(1)
Debug.WriteLine(new
{
where = "2) after await",
currentTs = TaskScheduler.Current,
thread = Thread.CurrentThread.ManagedThreadId,
context = SynchronizationContext.Current
});
}, CancellationToken.None, TaskCreationOptions.None, scheduler: ts).Unwrap();
}
}
}
调试输出中(点击该按钮时):
The debug ouput (when the button is clicked):
{ where = 1) before await, currentTs = System.Threading.Tasks.SynchronizationContextTaskScheduler, thread = 9, context = System.Windows.Forms.WindowsFormsSynchronizationContext }
{ where = 2) after await, currentTs = System.Threading.Tasks.ThreadPoolTaskScheduler, thread = 9, context = System.Windows.Forms.WindowsFormsSynchronizationContext }
的问题:为什么 TaskScheduler.Current
从 SynchronizationContextTaskScheduler
更改为<$ C C> ThreadPoolTaskScheduler $后等待
吗?
The question: Why is TaskScheduler.Current
changing from SynchronizationContextTaskScheduler
to ThreadPoolTaskScheduler
after await
here?
这主要表现该行为 TaskCreationOptions.HideScheduler
为计谋
延续,这是意想不到的和不受欢迎的,在我看来,
This essentially exhibits the behavior TaskCreationOptions.HideScheduler
for await
continuation, which is unexpected and undesirable, in my opinion.
这个问题已经触发了我的另一个问题:
This question has been triggered by another question of mine:
AspNetSynchronizationContext伺机在ASP.NET 的延续。
推荐答案
如果没有实际的任务的执行,那么 TaskScheduler.Current
是一样的 TaskScheduler.Default
。换句话说, ThreadPoolTaskScheduler
实际上既充当线程池的任务调度程序的和的值,意思是没有当前的任务调度程序。
If there is no actual task being executed, then TaskScheduler.Current
is the same as TaskScheduler.Default
. In other words, ThreadPoolTaskScheduler
actually acts both as the thread pool task scheduler and the value meaning "no current task scheduler".
在异步
委托的第一部分计划明确使用了 SynchronizationContextTaskScheduler
,并运行与UI线程上既是任务调度和同步内容。任务调度转发委托给同步上下文。
The first part of the async
delegate is scheduled explicitly using the SynchronizationContextTaskScheduler
, and runs on the UI thread with both a task scheduler and synchronization context. The task scheduler forwards the delegate to the synchronization context.
在计谋
捕捉它的上下文,它抓住了同步的情况下(而不是任务调度程序),并使用该syncctx恢复。因此,该方法继续被张贴到syncctx,这在UI线程上执行它。
When the await
captures its context, it captures the synchronization context (not the task scheduler), and uses that syncctx to resume. So, the method continuation is posted to that syncctx, which executes it on the UI thread.
在延续了UI线程上运行,它的表现非常相似的事件处理程序;委托是直接执行,而不是包裹在一个任务。如果检查 TaskScheduler.Current
在的button1_Click
的开始,你会发现它也是 ThreadPoolTaskScheduler
。
When the continuation runs on the UI thread, it behaves very similarly to an event handler; the delegate is executed directly, not wrapped in a task. If you check TaskScheduler.Current
at the beginning of button1_Click
, you'll find it is also ThreadPoolTaskScheduler
.
顺便说一句,我建议你把这种行为(执行代表直接,不是裹着任务)作为一个实现细节。
BTW, I recommend you treat this behavior (executing delegates directly, not wrapped in tasks) as an implementation detail.
这篇关于理解TaskScheduler.Current的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!