await 在异步操作后不恢复上下文?
await does not resume context after async operation?
我已经从 Noseratio 那里读到 this question,它显示了一个行为,其中 TaskScheduler.Current
在可等待对象完成其操作后与 不同 。
答案指出:
If there is no actual task being executed, then TaskScheduler.Current
is the same as TaskScheduler.Default
这是真的。我已经看到了 here :
TaskScheduler.Default
- Returns an instance of the
ThreadPoolTaskScheduler
TaskScheduler.Current
- If called from within an executing task will return the
TaskScheduler
of the currently executing task
- If called from any other place will return
TaskScheduler.Default
但后来我想,如果是这样,让我们做 创建一个实际的 Task
(而不仅仅是 Task.Yield()
)并测试它:
async void button1_Click_1(object sender, EventArgs e)
{
var ts = TaskScheduler.FromCurrentSynchronizationContext();
await Task.Factory.StartNew(async () =>
{
MessageBox.Show((TaskScheduler.Current == ts).ToString()); //True
await new WebClient().DownloadStringTaskAsync("http://www.google.com");
MessageBox.Show((TaskScheduler.Current == ts).ToString());//False
}, CancellationToken.None, TaskCreationOptions.None,ts).Unwrap();
}
第一个消息框是 "True",第二个是 "False"
问题:
如您所见,我确实创建了一个实际任务。
我能理解为什么第一个 MessageBox 产生 True
。那是因为 :
If called from within an executing task will return the TaskScheduler
of the currently executing task
而且那个任务确实有 ts
是发送的 TaskScheduler.FromCurrentSynchronizationContext()
但是为什么上下文没有保存在第二个消息框?对我来说,斯蒂芬的回答并不清楚。
附加信息:
如果我改写(第二个消息框):
MessageBox.Show((TaskScheduler.Current == TaskScheduler.Default).ToString());
它确实产生了 true
。但为什么 ?
混淆的原因如下:
- UI 没有 "special"
TaskScheduler
。 UI 线程上代码 运行ning 的默认情况是 TaskScheduler.Current
存储 ThreadPoolTaskScheduler
而 SynchronizationContext.Current
存储 WindowsFormsSynchronizationContext
(或相关的在其他 UI 应用中)
ThreadPoolTaskScheduler
in TaskScheduler.Current
并不一定意味着它是 TaskScheduler
用于 运行 当前代码段。它还表示 TaskSchdeuler.Current == TaskScheduler.Default
等 "there is no TaskScheduler
being used"。
TaskScheduler.FromCurrentSynchronizationContext()
不是 return "acutal" TaskScheduler
。它 return 是一个 "proxy" 将任务直接发布到捕获的 SynchronizationContext
。
因此,如果您 运行 在开始任务之前(或在任何其他地方)进行测试,您将得到与等待之后相同的结果:
MessageBox.Show(TaskScheduler.Current == TaskScheduler.FromCurrentSynchronizationContext()); // False
因为 TaskScheduler.Current
是 ThreadPoolTaskScheduler
而 TaskScheduler.FromCurrentSynchronizationContext()
return 是 SynchronizationContextTaskScheduler
.
这是您示例的流程:
- 您从 UI 的
SynchronizationContext
(即 WindowsFormsSynchronizationContext
)创建了一个新的 SynchronizationContextTaskScheduler
。
- 在
TaskScheduler
上使用 Task.Factory.StartNew
安排您创建的任务。因为它只是一个 "proxy",所以它将委托发布到 WindowsFormsSynchronizationContext
,后者在 UI 线程上调用它。
- 该方法的同步部分(第一个 await 之前的部分)在 UI 线程上执行,同时与
SynchronizationContextTaskScheduler
. 相关联
- 方法到达 await 并 "suspended" 同时捕获那个
WindowsFormsSynchronizationContext
。
- 当在 await 之后继续继续时,它被发布到
WindowsFormsSynchronizationContext
而不是 SynchronizationContextTaskScheduler
,因为 SynchronizationContext
具有优先权 (this can be seen in Task.SetContinuationForAwait
)。然后它 运行 定期在 UI 线程上,没有任何 "special" TaskScheduler
所以 TaskScheduler.Current == TaskScheduler.Default
.
因此,在使用 SynchronizationContext
的代理 TaskScheduler
上创建的任务 运行s 但等待后的继续发布到 SynchronizationContext
而不是TaskScheduler
.
我已经从 Noseratio 那里读到 this question,它显示了一个行为,其中 TaskScheduler.Current
在可等待对象完成其操作后与 不同 。
答案指出:
If there is no actual task being executed, then
TaskScheduler.Current
is the same asTaskScheduler.Default
这是真的。我已经看到了 here :
TaskScheduler.Default
- Returns an instance of the
ThreadPoolTaskScheduler
TaskScheduler.Current
- If called from within an executing task will return the
TaskScheduler
of the currently executing task- If called from any other place will return
TaskScheduler.Default
但后来我想,如果是这样,让我们做 创建一个实际的 Task
(而不仅仅是 Task.Yield()
)并测试它:
async void button1_Click_1(object sender, EventArgs e)
{
var ts = TaskScheduler.FromCurrentSynchronizationContext();
await Task.Factory.StartNew(async () =>
{
MessageBox.Show((TaskScheduler.Current == ts).ToString()); //True
await new WebClient().DownloadStringTaskAsync("http://www.google.com");
MessageBox.Show((TaskScheduler.Current == ts).ToString());//False
}, CancellationToken.None, TaskCreationOptions.None,ts).Unwrap();
}
第一个消息框是 "True",第二个是 "False"
问题:
如您所见,我确实创建了一个实际任务。
我能理解为什么第一个 MessageBox 产生 True
。那是因为 :
If called from within an executing task will return the TaskScheduler of the currently executing task
而且那个任务确实有 ts
是发送的 TaskScheduler.FromCurrentSynchronizationContext()
但是为什么上下文没有保存在第二个消息框?对我来说,斯蒂芬的回答并不清楚。
附加信息:
如果我改写(第二个消息框):
MessageBox.Show((TaskScheduler.Current == TaskScheduler.Default).ToString());
它确实产生了 true
。但为什么 ?
混淆的原因如下:
- UI 没有 "special"
TaskScheduler
。 UI 线程上代码 运行ning 的默认情况是TaskScheduler.Current
存储ThreadPoolTaskScheduler
而SynchronizationContext.Current
存储WindowsFormsSynchronizationContext
(或相关的在其他 UI 应用中) ThreadPoolTaskScheduler
inTaskScheduler.Current
并不一定意味着它是TaskScheduler
用于 运行 当前代码段。它还表示TaskSchdeuler.Current == TaskScheduler.Default
等 "there is noTaskScheduler
being used"。TaskScheduler.FromCurrentSynchronizationContext()
不是 return "acutal"TaskScheduler
。它 return 是一个 "proxy" 将任务直接发布到捕获的SynchronizationContext
。
因此,如果您 运行 在开始任务之前(或在任何其他地方)进行测试,您将得到与等待之后相同的结果:
MessageBox.Show(TaskScheduler.Current == TaskScheduler.FromCurrentSynchronizationContext()); // False
因为 TaskScheduler.Current
是 ThreadPoolTaskScheduler
而 TaskScheduler.FromCurrentSynchronizationContext()
return 是 SynchronizationContextTaskScheduler
.
这是您示例的流程:
- 您从 UI 的
SynchronizationContext
(即WindowsFormsSynchronizationContext
)创建了一个新的SynchronizationContextTaskScheduler
。 - 在
TaskScheduler
上使用Task.Factory.StartNew
安排您创建的任务。因为它只是一个 "proxy",所以它将委托发布到WindowsFormsSynchronizationContext
,后者在 UI 线程上调用它。 - 该方法的同步部分(第一个 await 之前的部分)在 UI 线程上执行,同时与
SynchronizationContextTaskScheduler
. 相关联
- 方法到达 await 并 "suspended" 同时捕获那个
WindowsFormsSynchronizationContext
。 - 当在 await 之后继续继续时,它被发布到
WindowsFormsSynchronizationContext
而不是SynchronizationContextTaskScheduler
,因为SynchronizationContext
具有优先权 (this can be seen inTask.SetContinuationForAwait
)。然后它 运行 定期在 UI 线程上,没有任何 "special"TaskScheduler
所以TaskScheduler.Current == TaskScheduler.Default
.
因此,在使用 SynchronizationContext
的代理 TaskScheduler
上创建的任务 运行s 但等待后的继续发布到 SynchronizationContext
而不是TaskScheduler
.