为什么在使用多个等待时 SynchronizationContext.Post() 仅被调用一次?
考虑以下示例:
async Task DoWork()
{
await Task.Run(() =>
{
for (int i = 0; i < 25; i++)
{
Console.WriteLine("Task run 1: " + Thread.CurrentThread.ManagedThreadId);
}
});
// The SynchronizationContext.Post() gets called after Run 1 and before Run 2
await Task.Run(() =>
{
for (int i = 0; i < 25; i++)
{
Console.WriteLine("Task run 2: " + Thread.CurrentThread.ManagedThreadId);
}
});
// I expect it to run after Run 2 and before Run 3 as well but it doesn't
await Task.Run(() =>
{
for (int i = 0; i < 25; i++)
{
Console.WriteLine("Task run 3: " + Thread.CurrentThread.ManagedThreadId);
}
});
}
我希望每次等待操作结束时都会调用 SynchronizationContext.Post()
,但在重写 Post()
之后,像这样
public class MySynchronizationContext
{
public override void Post(SendOrPostCallback d, object? state)
{
Console.WriteLine("Continuation: " + Thread.CurrentThread.ManagedThreadId);
base.Post(d, state);
}
}
安装this 在 Main()
的最开始处,
SynchronizationContext.SetSynchronizationContext(new MySynchronizationContext());
它只在第一个 Run()
完成后打印一次消息。
我认为这是因为 Task.Run()
可能会检测到它正在线程池线程上被调用,并且只重用当前线程,但情况似乎并非如此,因为我的一些测试导致 运行 2 和运行 3 在不同的线程上运行。
为什么await
ed 任务的完成仅在第一个await
之后运行?
Consider the following example:
async Task DoWork()
{
await Task.Run(() =>
{
for (int i = 0; i < 25; i++)
{
Console.WriteLine("Task run 1: " + Thread.CurrentThread.ManagedThreadId);
}
});
// The SynchronizationContext.Post() gets called after Run 1 and before Run 2
await Task.Run(() =>
{
for (int i = 0; i < 25; i++)
{
Console.WriteLine("Task run 2: " + Thread.CurrentThread.ManagedThreadId);
}
});
// I expect it to run after Run 2 and before Run 3 as well but it doesn't
await Task.Run(() =>
{
for (int i = 0; i < 25; i++)
{
Console.WriteLine("Task run 3: " + Thread.CurrentThread.ManagedThreadId);
}
});
}
I would expect a call to SynchronizationContext.Post()
to be made every time an await operation ends but after overriding the Post()
like this
public class MySynchronizationContext
{
public override void Post(SendOrPostCallback d, object? state)
{
Console.WriteLine("Continuation: " + Thread.CurrentThread.ManagedThreadId);
base.Post(d, state);
}
}
Installed like this at the very start of Main()
SynchronizationContext.SetSynchronizationContext(new MySynchronizationContext());
It only prints the message once, after the first Run()
is completed.
I assumed that's because Task.Run()
may detect that it's being called on a threadpool thread and just reuse the current thread but that seems not to be the case because some of my tests resulted in Run 2 and Run 3 running on different threads.
Why does the completion of an await
ed Task only runs after the first await
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
SynchronizationContext.SetSynchronizationContext
方法在当前线程上安装提供的SynchronizationContext
。为了使相同的SynchronizationContext
能够被后续的await
捕获并重用,SynchronizationContext
的实现必须确保在原始线程,或者将其自身安装到用于调用延续的任何其他线程上。您的实现 (
MySynchronizationContext
) 不会执行此操作。它只是将Post
调用委托给base.Post
,它调用ThreadPool
上的延续。MySynchronizationContext
实例未安装在任何ThreadPool
线程上,因此第二个await
找不到任何可捕获的内容,因此调用第二个延续在Task.Run
方法完成的任何线程上,它也是一个ThreadPool
线程。因此,本质上,您获得的行为与使用正确实现的SynchronizationContext
获得的行为相同,例如 Stephen Cleary 的AsyncContext
,并配置第一个await
ConfigureAwait(假)
。The
SynchronizationContext.SetSynchronizationContext
method installs the suppliedSynchronizationContext
on the current thread. In order for the sameSynchronizationContext
to be captured and reused by subsequentawait
s, the implementation of theSynchronizationContext
must ensure that the continuation is invoked on the original thread, or that it installs itself on any other thread that it uses for invoking the continuation.Your implementation (
MySynchronizationContext
) doesn't do that. It just delegates thePost
call to thebase.Post
, which invokes the continuation on theThreadPool
. TheMySynchronizationContext
instance is not installed on any of theThreadPool
threads, so the secondawait
finds nothing to capture, and so the second continuation is invoked on whatever thread theTask.Run
method completed, which is also aThreadPool
thread. So essentially you get the same behavior that you would get by using a properly implementedSynchronizationContext
, like Stephen Cleary'sAsyncContext
, and configuring the firstawait
withConfigureAwait(false)
.我最终自己弄清楚了。
问题似乎是我对通过
await
捕获当前SynchronizationContext
的理解无效。I ended up figuring it out on my own.
The problem seemed to be my invalid understanding of capturing current
SynchronizationContext
by theawait
.