在异步程序(例如,asyncio,twisted等)中,所有系统调用必须是非阻塞的。这意味着需要在主循环的每次迭代中执行非阻塞select
(或等效的东西)。这似乎比多线程方法更浪费,其中每个线程可以使用阻塞调用和休眠(不浪费CPU资源),直到套接字准备就绪。
这是否有时会导致异步程序比其多线程替代程序慢(尽管线程切换成本),或者是否有某种机制使这不是一个有效的问题?
答案 0 :(得分:1)
在单个线程程序中使用select
时,您不必持续检查结果。使用它的正确方法是让它阻塞,直到相关的I / O到达,就像在多线程的情况下一样。
但是,select
调用不是等待单个套接字(或其他I / O),而是获取相关套接字的列表,并阻塞直到它们中的任何一个被中断。
一旦发生这种情况,select
会唤醒并返回已准备就绪的套接字(或I / O)列表。编码器可以按照要求的方式处理这些准备好的套接字,然后,如果代码没有其他任何操作,它可能会启动select
的另一次迭代。
如您所见,不需要轮询循环;在一个或多个所需套接字准备好之前,程序不需要CPU资源。此外,如果几个套接字几乎准备就绪,那么代码会唤醒一次,处理所有这些代码,然后再次启动select
。除此之外,程序不需要几个线程的资源开销,您可以看到为什么这在OS资源方面更有效。
答案 1 :(得分:0)
在我的问题中,我将I / O处理分为两类:非阻塞select
表示的轮询和#34;回调"由阻止select
表示。 (阻塞select
会使线程休眠,因此它并不严格地说是回调;但从概念上讲它类似于回调,因为在I / O准备就绪之前它不会使用CPU周期由于我不知道确切的术语,我只会使用"回调")。
我认为异步模型不能使用"回调" I / O。在我看来,这个假设是不正确的。虽然异步程序不应该使用非阻塞select
,并且它也不能严格地请求来自操作系统的传统回调,但它当然可以为OS提供其主事件循环并说出一个协程,并要求操作系统当I / O套接字准备就绪时,使用该协程在该事件循环中创建任务。在I / O就绪之前,这不会使用任何程序的CPU周期。 (如果它使用轮询而不是I / O中断,它可能会使用OS内核的CPU周期,但即使使用多线程程序也是如此。)
当然,这要求操作系统支持程序使用的异步框架。它可能没有。但即使这样,添加一个使用单个独立线程并阻止select
与操作系统通信的中间层似乎也很简单,每当I / O准备就绪时,就会向程序创建一个任务。主事件循环。如果该层包含在解释器中,则该程序看起来完全异步。如果将该层添加为库,除了将同步I / O转换为异步I / O的简单附加线程外,该程序将在很大程度上是异步的。
我不知道这是否在python中完成,但从概念上看似乎是合理的。