除进程线程外,你不知道的协程和纤程

 

预备知识

进程(process)和线程(thread)是操作系统的基本概念, 网上有个很好的比喻来描述他们:

计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行 

单个CPU一次只能运行一个进程, 就像一次只能启用一个车间,其他进程处于非运行状态

一个进程可以包括多个线程,就像一个车间里,可以有很多工人。他们协同完成一个任务

问题引出


假如现在你要集成一个系统, 请求 API 后做一些事情, 但是你不想阻塞前线程怎么办?


你可能首先想到的就是开新线程,或者用异步回调的方式.

的确是个好办法,让我们来 new 一个 Thread 吧!

然后系统中出现了遍地的 Thread, 而且Thread 的创建和销毁又耗时又浪费资源.

你说为何不用线程池(Thread Pool)?

不错!解决方案又来了,马上用上线程池,各种优化的配置, 系统哗啦啦性能上来了~

然而好景不长,系统越做越大,性能却又告急了.一看 CPU,却远远没有到瓶颈的地步.

如果我们抛开代码和环境问题来看, 首先线程池不是万能的,线程的数量始终有上限, 另外线程调度和上下文切换的时候是系统级别的,必定会有时间和性能消耗.

这种情况下这,如果还有一种东西可以在一个线程里继续 new 一些新的线程来工作, 而这些新的线程并不占用其他资源, 还是属于当前的线程里面, 也就是全是当前线程的产物, 最好还不怎么耗性能,想new多少就 new 多少.那该多好啊!

有吗?

有的!答案就是纤程!

 

关于协程( Coroutine )和纤程( Fiber )

协程和纤程在系统层面有着细微的差别, 但就语言级别而言是一致的.

以 Java 为栗子,我们主要称为纤程.协程和纤程对操作系统而言是完全不存在的, 也就是说他们的调度完全是开发人员行为.

所以理论上来说,纤程不存在上下文切换,不管创建多少纤程,都不会消耗太多性能,并且能让每个线程都处于运行状态,这样就可以充分利用资源,让系统 CPU 处于饱满状态.

既然操作系统不支持纤程, 那我们如何使用它呢?

事实上,大多数语言有自身的支持,比如 Go,Lua 等等,对于 Java 来说只能借助第三方框架实现.就最近比较火的 Quasar 为栗子吧,它的写法几乎和线程一样.

Quasar 的使用

首先引入 maven 依赖

<properties>
    <quasar-version>0.7.5</quasar-version>
    <quasar-groupId>co.paralleluniverse</quasar-groupId>
    <javaagent>{your-quasar-path}/quasar-core-0.7.5.jar</javaagent>
</properties>
<dependency>
    <groupId>${quasar-groupId}</groupId>
    <artifactId>quasar-core</artifactId>
    <version>${quasar-version}</version>
</dependency>
<dependency>
    <groupId>${quasar-groupId}</groupId>
    <artifactId>quasar-actors</artifactId>
    <version>${quasar-version}</version>
</dependency>
<dependency>
    <groupId>${quasar-groupId}</groupId>
    <artifactId>quasar-galaxy</artifactId>
    <version>${quasar-version}</version>
</dependency>
<dependency>
    <groupId>${quasar-groupId}</groupId>
    <artifactId>quasar-reactive-streams</artifactId>
    <version>${quasar-version}</version>
</dependency>
<dependency>
    <groupId>${quasar-groupId}</groupId>
    <artifactId>quasar-kotlin</artifactId>
    <version>${quasar-version}</version>
</dependency>

再引入 Java Agent 插件

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId> <!-- Run with "mvn compile maven-dependency-plugin:properties exec:exec" -->
    <version>1.3.2</version>
    <configuration>
        <mainClass>com.test.Main</mainClass>
        <workingDirectory>target/classes</workingDirectory>
        <executable>java</executable>
        <arguments>
            <!-- Turn off before production -->
            <argument>-Dco.paralleluniverse.fibers.verifyInstrumentation=true</argument>
            <!-- Quasar Agent -->
            <argument>-javaagent:${javaagent}</argument>
            <!-- Classpath -->
            <argument>-classpath</argument> <classpath/>
            <!-- Main class -->
            <argument>com.test.Main</argument>
        </arguments>
    </configuration>
</plugin>

之后就可以编码了:

new Fiber<Void>(() -> {
    Strand.sleep(3000);
    System.out.println("start...");
}).start();

附上Github 地址:

http://github.com/puniverse/quasar

Quasar 的优势和实现原理

1.异步不阻塞

2.不用切换线程,造成不必要的开销

3.理论上可以创建百万级别的 Fiber 达到高并发

4.不用担心调度问题,交给 Quassar

5.像写同步代码一样去写异步代码吧!

你可以想象,Quasar 其实通过 Java Agent 操作了字节码,把整个代码拆分成了很多你需要运行的代码块,然后放入一个大大的 switch case里面,当代码遇到阻塞的时候,跳去执行其他代码,当回调或响应的时候再跳回去继续执行.所以一个线程其实可以一直虚拟并发运行着,CPU 打满杠杠的!

 

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

Scroll to Top