我对这里的术语有点困惑。让一个程序由一些概念上不同的任务组成:
在异步编程模型中,任务相互交错,但在一个控制线程中。即使在多处理器系统上,单线程异步系统也将始终以交错方式执行。没有实际的并行性。
事件驱动的方法是做“异步编程”的唯一方法吗?
我对这里的术语有点困惑。让一个程序由一些概念上不同的任务组成:
在异步编程模型中,任务相互交错,但在一个控制线程中。即使在多处理器系统上,单线程异步系统也将始终以交错方式执行。没有实际的并行性。
事件驱动的方法是做“异步编程”的唯一方法吗?
简短的回答是:还有其他方法。
长答案:这取决于您使用的是什么技术。例如,JS 不提供任何非事件异步方法(至少,我知道)。但是,在 C 和许多其他语言中,您可以在用户进程中使用信号来进行异步编程。
事件和非事件异步编程之间的区别在于,事件编程是对较低级别的非事件样式的包装,它简化了在什么上下文中可能发生的事情的推理,并有助于限制堆栈深度。
轻松推理:假设您正在编写一个程序,该程序执行一些 I/O 并在 I/O 完成时发送一个信号。在您的信号处理程序中,您可以立即处理该 I/O,但您必须小心您在处理程序中所做的事情。例如,如果你需要一个锁,你必须知道被中断的线程(你刚刚征用其堆栈的那个)没有持有锁,否则你将立即导致死锁。另外,如果你抓住锁然后另一个 I/O 完成会发生什么?另外,如果被信号中断的线程具有非常高的优先级并且您的处理程序导致它等待很长时间,该怎么办?基本上答案是:“永远不要在信号处理程序中做任何需要同步的事情。”
限制堆栈深度:正如我已经提到的,多个信号处理程序可以同时在同一个堆栈上运行(后面的中断前面的)。如果您经常收到信号,您可能会陷入堆栈溢出的不幸境地。出于这个原因,让信号处理程序保持简短和甜蜜是一个非常好的主意。事件编程通过只做一个非常简单的操作来实现这一点:将任务添加到队列中。
事件编程的缺点是如果天真地使用它可能会变慢。例如,假设您的任务非常大,但每隔一段时间用户按下一个按钮,屏幕上就会出现一个字母。如果您在信号处理程序中处理按钮按下,它可以立即出现在屏幕上。如果将其添加到队列中,可能需要一段时间才能真正处理按钮按下事件。您可以在某些事件编程框架中赋予事件优先级来处理这个问题,但最佳实践是让所有事件任务非常短,并在单独的线程池中运行任何长时间运行的东西。这(以及其他原因)是事件框架几乎总是依赖异步 I/O 的原因。