C#中的异步操作和线程

6
异步编程是一种技术,它在后台调用长时间运行的方法,以便UI线程保持响应性。在调用Web服务或数据库查询或任何I/O绑定操作时应使用此技术。当异步方法完成时,它将结果返回给主线程。通过这种方式,程序的主线程不必等待I/O绑定操作的结果,并且可以继续执行而不会阻塞/冻结UI。这样做没问题。
据我所知,异步方法在后台工作线程上执行。运行时从线程池中提供线程,或者可能为其执行创建全新线程。
但我已经读过许多帖子,其中异步操作可能在不使用任何线程的情况下执行。现在我非常困惑。
1)您能否帮助澄清在什么情况下异步操作将不使用线程?
2)处理器核心在异步操作中的作用是什么?
3)它与多线程有何不同? 我知道一件事,即多线程对于计算密集型操作很有用。
请帮忙解答。

C# 中有多种异步方式 - 你是指异步编程模型(或 Begin/End 风格)、基于事件的异步模式(完成 EventHandler)还是任务异步模式(async/await/Task 风格)? - Jeffrey Hantin
我有一篇博客文章,详细描述了异步操作如何在不阻塞线程的情况下工作 - Stephen Cleary
嗨,Jeffrey,无论是APM、EAP还是TAP,我在某处读到EAP使用后台线程。 - Tisha Anand
但是 TAP 可能会或可能不会使用线程池线程。 - Tisha Anand
谢谢Jeffery...你的博客文章帮了我很多。谢谢。 - Tisha Anand
3个回答

5

IO (假设是网络上的数据库操作) 是这三个方面的一个很好的例子:

  1. 你基本上只需注册一个回调函数,当 IO 操作完成时,操作系统将最终调用该函数(可能在新创建的线程上)。没有线程一直等待 - 复活将由硬件事件触发(或者至少通常在用户空间之外的操作系统进程中触发)

  2. 它可能没有(见第1点)

  3. 在多线程中,您使用不止一个线程(您的后台线程),其中一个可能会空闲无事可做(但使用系统资源) - 如果您某些计算(因此线程不是空闲等待外部结果 ),那么使用后台工作线程就有意义。


嗨,Carsten,你的意思是说在异步IO绑定操作的情况下,执行不会在后台线程上完成。操作系统仅使用线程池线程来调用回调方法? - Tisha Anand
是的,没错 - 如果做得正确的话。这就是为什么我说要异步扩展 - Random Dev
好的,非常感谢。但是在您最后的评论中还有一个条件,“如果做得正确”。这是什么意思?如果异步IO绑定操作不在线程上执行,那它在哪里执行?在另一个操作系统进程中吗?如果是这样,那么必须在另一个操作系统进程中有专用的线程? - Tisha Anand
不需要在线程中。基本上,当操作系统在您的网络硬件发出新数据的信号后恢复您的计算时,就足够了,操作系统会检查...细节非常复杂,但基本上只要有硬件中断,您就不必一直观察;)-如果正确执行,则意味着必须正确执行OS、框架等的实现-例如,我认为Task.Delay仍将使用线程/计时器,因此即使它看起来是异步的,您也不会真正获得(全部)好处。 - Random Dev
非常感谢你,Carsten...你帮了我很多 :) - Tisha Anand

1
异步操作并不意味着它们将如何被处理,只是表明它们希望能够稍后返回结果。例如:
  • 它们可能(正如您所提到的)将计算密集型任务拆分到独立线程中,但这不是唯一的用例。
  • 如果已经有足够的缓冲内容(输入)或空闲缓冲区空间(输出)来服务于 I/O 请求,则它们有时会在启动它们的调用中同步完成,此时不使用额外的线程。
  • 它们可能仅向系统发送一个长时间运行的 I/O 请求;在这种情况下,回调很可能会在接收到 I/O 完成端口的通知后在后台线程上发生。
  • 完成后,回调可能稍后在同一线程上交付;这在 UI 框架内的事件中特别常见,例如在 WebBrowser 中导航。

0

异步性并不代表线程。它是指具有某种回调的状态机(不完全正确,但您可以将其视为事件)内进行处理。异步性不会引发线程,也不会显著分配系统资源。您可以运行任意数量的异步方法。

线程确实会对您的系统产生影响,并且您一次最多只能拥有一定数量的线程。

I/O 操作主要与其他控制器(如硬盘驱动器、网络适配器等)相关。如果您创建一个线程,则应用程序的线程无事可做,一直等待控制器完成操作。而在异步中,正如 Carsten 和 Jeffrey 已经提到的那样,您只需要获取某种回调机制,因此您的线程可以继续执行其他工作、方法等。

还要记住,每个线程都需要资源(RAM、性能、垃圾回收句柄变得更糟糕等),甚至可能会出现异常(OutOfMemoryException 等)。

那么何时使用线程呢?除非真的需要,否则绝对不要使用。如果有异步 API,请使用它,除非您有非常重要的理由不使用它。过去异步 API 真正让人头疼,这就是为什么许多人在只需要异步性时就使用线程的原因。

例如,Node.js 完全拒绝使用多线程!

这对于处理多个请求的情况尤其重要,例如在服务/网站中总是有工作要做。还有一个Jeffrey Richter的网络研讨会,对此有所帮助。
另外,请查看MSDN文章 PS:异步和等待的源代码往往更易读

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