真正的多线程和协程谁的效率高(知识科普比多线程还快)

什么是线程

线程是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

在一些系统中,线程也被称之为轻量进程。而轻量进程一般指的内核线程。

一个进程可以有很多线程来处理,每条线程并行执行不同的任务。如果进程要完成的任务很多,这样需很多线程,也要调用很多核心。因此在多核的CPU上面使用多线程程序设计能更有效地利用CPU。

多线程示例:

@Test public void testThread() { Thread thread1 = new Thread(() -> funA(),"Thread-1"); Thread thread2 = new Thread(() -> funB(),"Thread-2"); Thread thread3 = new Thread(() -> funC(),"Thread-3"); thread1.start(); thread2.start(); thread3.start(); } public void funA() { System.out.println("A1"); System.out.println("A2"); System.out.println("A3"); } public void funB() { System.out.println("B1"); System.out.println("B2"); System.out.println("B3"); } public void funC() { System.out.println("C1"); System.out.println("C2"); System.out.println("C3"); }

运行结果:

A1 C1 C2 C3 B1 A2 A3 B2 B3

线程调度示意图:

真正的多线程和协程谁的效率高(知识科普比多线程还快)(1)

如上图,一个线程绑定一个任务,任务会在CPU内执行,在Windows和Liunx等系统中,任务的调度采用的时间片强占调用方式。任务的状态分为运行状态和就绪状。举个例子,当任务1进行到就绪状态时,就会进行中断处理,并保存当前任务相关的执行信息(存在内存中),操作系统内核会分配接下来可以执行哪个任务,我们假设分配给了任务3执行。当任务3中断了,可能跳到了任务2,也可能跳回到了任务1,读取保存的执行信息,继续执行起来。

这里就会有3个线程之间的来回切换。

线程阻塞切换需要在操作系统的内核上面切换,会有用户态到内核态的切换。

协程与线程

上面描述我们知道,线程中任务的调度是由内核操作的,会有用户空间到内核空间的跨越,不利于系统性能。而聪明的程序员想到,何不自己写一套程序来控制任务之间的调度呢,这样将任务调度控制在用户空间。

关于协程,我们来看看维基百科上面关于协程的介绍:

协程非常类似于线程。但是协程是协作式多任务的,而线程典型是抢占式多任务的。这意味着协程提供并发性而非并行性。协程相比线程,优势是它们可以用于硬性实时的语境(在协程之间的切换不需要涉及任何系统调用或任何阻塞调用),这里不需要用来守卫关键区块的同步性原语(primitive)比如互斥锁、信号量等,并且不需要来自操作系统的支持。有可能以一种对调用代码透明的方式,使用抢占式调度的线程实现协程,但是会失去某些利益(特别是对硬性实时操作的适合性和相对廉价的相互之间切换)。

线程是协作式多任务的轻量级线程,本质上描述了同协程一样的概念。其区别,如果一定要说有的话,是协程是语言层级的构造,可看作一种形式的控制流,而线程是系统层级的构造,可看作恰巧没有并行运行的线程。这两个概念谁有优先权是争议性的:线程可看作为协程的一种实现,也可看作实现协程的基底。

真正的多线程和协程谁的效率高(知识科普比多线程还快)(2)

一个线程其实就是执行一个子程序,那么什么什么是子程序呢?

子程序就是函数,在Java中就是方法。如果一个funA调用了funB,funB调用funC,那么,需要先执行完funC,再执行funB,最后再执行funA。

整个子程序内部调用是通过栈来实现的。

那么协程跟子程序有什么关系呢?

其实协程也是一个子程序,但是在执行子程序的过程中,子程序的内部可以中断,转而去执行别的子程序,之后又可以回来执行当前的子程序。

这个是不是有点像CPU的中断。

我们来看一段代码:

funA() { println("A1"); println("A2"); println("A3"); } funB() { println("B1"); println("B2"); println("B3"); } funC() { println("C1"); println("C2"); println("C3"); }

如果上面的代码由协程执行,可能在执行funA的时候在内部某一点中断,然后就去执行funB,也有可能回去执行funC,最后的结果可能就是:

A1 A2 C1 C3 A3 B3 B2 C2 B1

在这里要注意的是3个函数之间是没有互相调用的。这里的显示也并没有使用多线程技术,而只是用了一个线程执行的。

既然多线程也能实现上面的功能,为什么还要诞生协程呢?

在协程中,子程序的切换不是线程间的切换,而是由程序本身去控制。所以相对于多线程,没有了线程之间来回切换的开销。特别是在线程数量很多的情况下。

如果在多线程中我们需要要操作共享资源,我们就需要使用锁,我们可能会使用各种不同级别的锁,甚至会使用操作系统层面的锁。如果是在协程中,就不需要使用锁了。

总的来说,协程提高了程序的执行的效率。如果我们的系统是多核的,我们可以利用多核加上协程最大程度地发挥系统性能。

有关协程的发展史

协程的概念最早在20世纪90年代就诞生了,而线程的概念是在80年代才提出的,就使用情况来说,协程的使用远远不如线程广泛。

近些年,一些编程语言的新贵Go和Kotlin纷纷引入了协程这个语言特性,使得协程这个似乎十分陌生的概念开始频繁进入大家的视野。

相信对于从事Java开发的程序员来说,没有听过协程也很正常,我也是在网上看到一篇腾讯的面试攻略,才知道有协程这个东西。

如果你掌握的语言是Go,Kotlin或者Python等语言,那就该了解下协程。大厂比较喜欢面试这些东西。

好了,如果别人问你什么是协程,你可以说:协程是一种轻量级线程。

参考

1.协程 - 维基百科,自由的百科全书 (wikipedia.org)-https://zh.wikipedia.org/wiki/协程

2.多线程 - 维基百科,自由的百科全书 (wikipedia.org)-https://zh.wikipedia.org/wiki/多线程

3.协程 - 廖雪峰的官方网站 (liaoxuefeng.com)-https://www.liaoxuefeng.com/wiki/897692888725344/923057403198272

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页