好吧......我在这里有点困惑。 async
monad允许你使用let!
来开始计算给定的异步方法,并暂停线程,直到结果可用..这一切都很好,我明白了。
现在我不明白为什么他们为WebClient
类做了扩展,那个名为AsyncDownloadString
的那个 - 你能不能将普通的DownloadString
包装在异步块中?我很确定,我在这里错过了一个重要的观点,因为我已经完成了一些测试,显示DownloadString包含在异步块中,仍会阻塞该线程。
答案 0 :(得分:6)
两者之间存在重要差异:
DownloadString
方法是同步的 - 调用该方法的线程将被阻止,直到整个字符串被下载(即直到整个内容通过互联网传输)。
另一方面,AsyncDownloadString
不会长时间阻塞该线程。它要求操作系统启动下载,然后释放该线程。当操作系统接收到某些数据时,它从线程池中选择一个线程,该线程将数据存储到某个缓冲区并再次释放。下载所有数据后,该方法将从缓冲区读取所有数据并恢复其余的异步工作流程。
在第一种情况下,线程在整个下载过程中被阻止。在第二种情况下,线程只在很短的时间内处理(处理收到的响应时,但在等待服务器时则不行。)
在内部,AsyncDownloadString
方法只是DownloadStringAsync
的包装,因此您还可以在MSDN documentation中找到更多信息。
答案 1 :(得分:3)
需要注意的重要一点是,异步编程是关于执行不 CPU绑定的操作,即IO绑定的操作。这些IO绑定操作在IO线程上执行(使用操作系统的重叠IO功能)。这意味着即使你在异步块中包含一些阶乘函数并使用let!
绑定在另一个异步块内运行它,你也不会从中获得任何好处,因为它将在CPU绑定线程上运行并且执行异步编程的主要目的是在IO性质的事情上不占用CPU绑定线程,因为在此期间IO完成时该CPU绑定线程可用于其他目的。
如果你看一下.NET中的各种IO类,比如File,Socket等。它们都有阻塞和非阻塞的读写操作。阻塞操作将等待IO在CPU线程上完成,从而阻塞CPU线程直到IO完成,其中非阻塞操作使用重叠的IO API调用来执行操作。
Async有一个方法可以从Files,Socket等非阻塞API中创建异步块。在你的情况下,调用DownloadString会阻塞CPU线程,因为它使用基础类的阻塞API,其中{{1} }使用非阻塞 - 基于重叠的API调用。