自从我开始编程以来,我总是一致认为线程阻塞是不好的——有些操作本质上是异步的,我们应该创建尊重这种条件的代码。这就是我们如何进入回调地狱,这就是 Promise API 的发明方式,这就是 Rx 的出现方式,这就是 async-await 如何通过 C# 流行起来,等等。
但是,我选择的语言对异步等待和协程的支持非常差,我想知道 - 通过将所有异步操作通过阻塞转换为同步来以同步方式编写 iOS 或 Android 应用程序会产生什么后果?
我的理由是:
在现代,ZeroMQ 等库很好地满足了 Actor 模型编程的需要(它对于进程间和进程内 Actor 模型架构都很有效,并且非常多平台)。通信顺序过程是其演变,并体现在 Go 和 Rust 等语言中。如果您愿意,您可以在 Actor 之上实现 CSP。
无论是 Actor 还是 CSP,基本上都是按照你的建议做的;每个线程或进程都是严格顺序的,它们都在等待消息到达,或者忙于处理最后一条消息或其他一些阻塞操作。极端情况下,最终可能会进行大量上下文切换,但话又说回来,如果要大量使用 async/await,那么无论如何都会发生相当多的上下文切换。
就我个人而言,我更喜欢Actor或CSP的明确性;存在的线程是我启动的线程,而不是由某些复杂的异步编程范例中途生成的线程,或者未能从超额订阅池中分配出来的线程。
显然,如果您在运行时大量创建和转储线程,那么这将是昂贵的,但通常大多数应用程序问题都可以通过一组线程来解决,这些线程在程序执行时启动一次,直到程序需要时才会终止。退出。
Actor 和 CSP 之间的区别在于,前者进程/线程之间的消息传输是异步的,而在 CSP 中它们是同步的,也称为“执行集合点”。在 CSP 中,只有接收者收到所有发送的数据后,发送才会完成。 CSP 是我更喜欢的,因为它不会让你隐藏“延迟”的东西。
例如,假设您有一个主线程,它向 10 个提取线程发出 1000 个提取请求,每个提取请求完成后都会返回一个结果。使用 Actor 模型,您可以非常快速地将 1000 个提取请求排队。最终结果将开始慢慢出现。使用 CSP,如果这些获取器落后于曲线,主线程就会延迟;关键是,根本没有足够的获取器来满足需求,而 CSP 使这一点显而易见,而 Actor 模型可以(暂时)掩盖问题。
CSP 的另一个好处是,如果您编写了一个可能出现死锁的循环,那么无论发生什么情况,它每次都会出现死锁。而使用 Actor 模型,一切看起来都很好,直到网络交换机变得有点慢......
不管怎样,这就是我的2分钱! Actor 和 CSP 都是老想法 - 1970 年代。