为什么我们对阻塞线程如此警惕?

问题描述 投票:0回答:1

自从我开始编程以来,我总是一致认为线程阻塞是不好的——有些操作本质上是异步的,我们应该创建尊重这种条件的代码。这就是我们如何进入回调地狱,这就是 Promise API 的发明方式,这就是 Rx 的出现方式,这就是 async-await 如何通过 C# 流行起来,等等。

但是,我选择的语言对异步等待和协程的支持非常差,我想知道 - 通过将所有异步操作通过阻塞转换为同步来以同步方式编写 iOS 或 Android 应用程序会产生什么后果?

我的理由是:

    我会非常小心地管理我的线程,永远不会在主线程上做任何事情,总是会相应地并尽可能少地进行上下文切换
  • 我的应用程序不是每秒 1000 个请求的服务器,并且在任何给定时刻都不会有超过 2 个异步 I/O 操作,因此我不会耗尽线程
那么我阻塞了几个线程——这会给我编写完全同步代码的好处!那么还有其他原因导致这样做不可接受吗?我正在寻找一些超出普遍理解的原因,比如操作系统是否会讨厌我的进程阻塞线程?或者,这在某种程度上效率很低?

multithreading asynchronous blocking
1个回答
0
投票
您可能想考虑诸如演员模型编程之类的东西,甚至更好的通信顺序过程。

在现代,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 年代。

© www.soinside.com 2019 - 2024. All rights reserved.