通过现实世界的比较,它帮助我理解事物,在这里是快餐。
在java中,对于同步阻塞,我理解线程处理的每个请求一次只能完成一个。就像通过一个直行公路订购一样,如果我排在第十位,我就得等前面的9辆车。但是,我可以打开更多线程,以便同时完成多个订单。
在javascript中,可以使用异步非阻塞但单线程。据我所知,会发出多个请求,并且这些请求会立即被接受,但是在返回之前,请求会在稍后的某个时间由某个后台进程处理。我不明白这怎么会更快。如果你同时点了10个汉堡,这10个请求将立即被提交,但由于只有一个厨师(单线程),所以创建这10个汉堡仍然需要相同的时间。
我的意思是,我理解为什么非阻塞异步单线程在某些方面“应该”更快,但是我问自己的问题越多,我就越不理解它,这使我不理解它。
我真的不明白,对于任何类型的应用程序,包括io,非阻塞异步单线程如何比同步阻塞多线程更快。
2条答案
按热度按时间inkz8wg91#
非阻塞异步单线程有时更快
那不太可能。你从哪弄来的?
在多线程同步i/o中,其工作原理大致如下:
操作系统和appserver平台(例如jvm)共同创建10个线程。这些是在内存中表示的数据结构,在kernel/os级别运行的调度器将使用这些数据结构来告诉一个cpu内核“跳到”代码中的某个点,以运行在那里找到的命令。
表示线程的数据结构或多或少包含以下项:
我们运行的指令在内存中的位置是什么
整个“堆栈”。如果某个函数调用了第二个函数,那么我们需要记住所有的局部变量以及我们在原始方法中所处的位置,这样当第二个方法“返回”时,它就知道该怎么做了。e、 一般的java程序大概有20个方法深,这是本地变量的20倍,代码中有20个地方需要跟踪。这都是成堆的。每根线都有一根。对于整个应用程序,它们的大小往往是固定的。
在运行此代码的内核的本地缓存中启动了哪些缓存页?
线程中的代码编写如下:与“资源”交互的所有命令(比cpu慢几个数量级);认为网络数据包、磁盘访问等)被指定为立即返回请求的数据(仅当您请求的所有内容都已可用且在内存中时才可能)。如果这是不可能的,因为您想要的数据还不在那里(比如说,携带您想要的数据的数据包仍然在电线上,指向您的网卡),那么只有一件事可以为“获取我的网络数据”功能提供动力的代码做:等待数据包到达并进入内存。
为了不只是什么都不做,os/cpu将一起工作来获取表示线程的数据结构,冻结它,找到另一个这样的冻结数据结构,解冻它,并跳转到代码中的“我们把东西放在哪里了”点。
这是一个“线程开关”:核心a运行的是线程1。现在核心a正在运行线程2。
线程切换涉及移动一堆内存:所有那些“活动”的缓存页和堆栈都需要靠近内核,cpu才能完成任务,因此cpu从主内存装入一堆页,这确实需要一些时间。不是很多(纳秒),但也不是零。现代的cpu只能对附近cachepage中加载的数据进行操作(cachepage的大小约为64k到1mb,仅此而已,比ram存储的容量少1000多倍)。
在单线程异步i/o中,其工作原理大致如下:
当然还有一个线程(所有的东西都在一个线程中运行),但这次讨论的应用程序根本就没有多线程。相反,它本身创建了跟踪多个传入连接所需的数据结构,关键的是,用于请求数据的原语的工作方式不同。请记住,在同步情况下,如果代码要求从网络连接获得下一组字节,那么线程将最终“冻结”(告诉内核找到其他要做的工作),直到数据存在为止。在异步模式下,如果数据可用,则返回数据,如果不可用,则返回函数“give me some data!”还是回来了,只是说:对不起,巴德。我有0个新字节给你。
应用程序本身将决定在其他连接上工作,这样,一个线程就可以管理一堆连接:连接1有数据吗?是的,太好了,我会处理的。不?哦,好吧。是否有连接2的数据?以此类推。
请注意,如果数据到达连接#5,那么这一个线程要处理传入的数据,可能需要从内存加载一组状态信息,并可能需要写入它。
例如,假设您正在处理一个图像,一半的png数据到达了网络。你不能用它做很多事情,所以这一个线程将创建一个缓冲区,并在其中存储一半的png。当它跳转到另一个连接时,它需要加载它所得到的15%的图像,并将刚刚到达网络数据包的10%的图像添加到缓冲区中。
这个应用程序同样会导致大量内存被移入和移出缓存页,所以从这个意义上说,这并不是完全不同的,如果你想一次处理10万件事情,你将不可避免地不得不移入和移出缓存页。
那么有什么区别呢?你能用炒菜的术语来形容吗?
不完全是,不。都是数据结构。
关键的区别在于什么被移入和移出这些缓存页。
在异步的情况下,它正是您编写的代码想要缓冲的。不多不少。
在synchronous的情况下,就是“表示线程的数据结构”。
以java为例:这至少意味着该线程的整个堆栈。这取决于
-Xss
参数,大约值128k的数据。因此,如果同时处理10万个连接,那么对于这些堆栈来说,就需要12.8gb的ram!如果这些传入图像的大小都只有4k左右,那么您可以使用4k缓冲区来完成,最多只需要0.4gb的内存,如果您通过异步方式手动实现的话。
这就是异步的好处所在:通过手动滚动缓冲区,您无法避免将内存移入或移出缓存页,但您可以确保它是较小的块。这样会更快。
当然,要真正加快速度,异步模型中存储状态的缓冲区需要很小(如果您需要在对其进行操作之前将128k存储到内存中,这一点并不重要,因为这些堆栈已经很大了),而且您需要一次处理这么多事情(10k+同时处理)。
我们不使用汇编语言编写所有代码,或者内存管理语言流行的原因之一是:处理此类问题既繁琐又容易出错。除非利益明确,否则你不应该这样做。
这就是为什么synchronous通常是更好的选择,实际上,通常更快(那些os线程调度程序是由Maven程序员编写的,并且调整得非常好)。你没有机会复制他们的工作)-这就是“通过手动滚动缓冲区,我可以减少需要移动一吨的字节数!”事情要比损失更重要。
另外,异步作为一种编程模型是复杂的。
在异步模式下,您永远不能阻止。想快速查询数据库吗?这可能会阻塞,所以你不能这样做,你必须把你的代码写成:好的,把这个任务打掉,当它回来的时候,这里有一些代码要运行。您不能“等待答案”,因为在异步区域中,不允许等待。
在异步模式下,无论何时请求数据,都需要能够处理所需数据的一半。在同步模式下,如果你要求4k,你得到4k。在这个任务中,你的线程可能会冻结,直到4k可用,这一点你不必担心,你编写代码的时候,就好像它只是按你的要求来的,完成了。
bbbuutt。。。油炸厨师!
听着,cpu的设计还不够简单,不能用在这样的餐厅里。
b0zn9rqh2#
你在思想上把瓶颈从你的过程(汉堡订购者)转移到另一个过程(汉堡制造商)。
这不会使您的应用程序更快。
在考虑单线程异步模型时,真正的好处是您的进程在等待另一个进程时不会被阻塞。
换句话说,不要将async与单词fast联系起来,而是与单词free联系起来。自由做其他工作。