WebClient在哪个线程上引发其事件?

11

我找不到任何文件明确规定WebClient在哪个线程触发事件。 我进行了一些测试,得出了以下结论:

  • 如果从UI线程调用(例如从事件处理程序中调用),则事件处理程序将在该线程上执行。 作为测试,在调用OpenReadAsync之后添加了一个无限循环。 事件处理程序从未被调用。

  • 如果没有UI线程,例如在控制台应用程序中,则事件处理程序将在线程池线程上执行。 在这种情况下,如果我想要向应用程序的其余部分提供一些结果,我必须注意线程问题。

这种行为是否有记录? 我什么也没找到。

对于C#的新异步特性,我基本上有同样的问题-最终,异步代码将必须执行。 当没有UI线程时,它会生成线程池线程吗? 这反过来是否需要线程安全的代码?

我觉得我在这里缺少了什么-我只能找到非常少的信息,但这对我来说似乎很重要。


很好的问题!我也遇到了WebClient的同样问题,当我得出与你相同的结论时,我开始搜索。 - anton.burger
2个回答

5
对于WebClient,我也没有找到详细的文档,但我见过与你相同的行为。基本上,这可以描述为“如果在调用开始时存在活动同步上下文,则使用它 - 否则使用线程池”。
对于C# 5中的异步行为,这取决于您正在等待的内容的实现...但我相信Task<T>的等待者将使用TaskScheduler.Current来安排一个延续 - 这意味着您会看到相同类型的行为。(它不仅仅是界面线程设置任务计划程序,但这是最明显的例子。)
当使用线程池线程时,它应该仍然是线程安全的 - 方法一次只在单个线程中执行,并且我相信任务并行库执行所有必需的内存屏障。
如果您对异步操作的背后原理感兴趣,您可能想阅读我的Eduasync博客系列

+1 我猜我缺少的是CLR中已经有很多关于线程和同步的基础设施,可以假设WebClient使用它们。EduaAsync的帖子非常有趣。 - TheFogger

3
WebClient类实现了基于事件的异步模式。该模式在框架设计准则中有详细描述,但MSDN也提供了一些关于如何实现它的提示。
该模式的实现者使用AsyncOperationManager创建每个异步操作的AsyncOperation,并使用AsyncOperation.Post Method引发事件。Post Method在创建AsyncOperation时,将传递的回调函数在SynchronizationContext上执行,该上下文是当前的。
在WinForms或WPF应用程序中,默认的同步上下文是UI线程,在控制台应用程序中则为null。在后一种情况下,WebClient Class显然选择在ThreadPool线程中引发事件,但这是实现细节。

3
+1 好链接。然而,我不同意线程行为是一种实现细节的说法。对于事件处理程序的作者来说,这是重要信息。 - TheFogger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接