4

协程难理解?那么就来玩一玩分手厨房游戏

 2 years ago
source link: https://zhuanlan.zhihu.com/p/430372196
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

协程难理解?那么就来玩一玩分手厨房游戏

CrackingOysters,源于编程珠玑,也是公众号

之前写了Coroutine, 异步,同步,async, await
本文初版受公司之邀编写,有13000的展示量,现整理精简如下。

引言

如果你还在想办法理解协程 (coroutine)是什么,那么就让我们玩一玩分手厨房~

分手厨房(overcooked),是一款多人烹饪游戏,玩家需要在特定的时间内做出尽可能多的订单。

据说玩了的情侣会分手,所以又名分手厨房。你要不要想挑战一下(*^▽^*)

协程 (coroutine),不仅让你的代码逼格更高,也能让你的程序在特定场景下性能更好!

有些人花了很多时间并不一定能理解它。而游戏,却很容易理解,所以本文通过分手厨房游戏来帮助理解什么是协程,提升逼格。 (如果你已经理解协程,那么就来看看分手厨房是怎么玩的吧)

Overcooked

先让我们来看看分手厨房的玩法——

v2-8edf26cfd5fd9acd7d4f5bba7d81d80b_720w.jpg

玩家们分别控制着带厨师帽的小人。没有厨师帽的是一些NPC (no player character)。如上图,我们一共有四个玩家,对应着四个厨师。

游戏开始后,左上角会不停地出现订单,而玩家们通过不停地完成这些订单得分。 时间截止后,分数达到一定,即可进入下一关。

v2-3cdd618185f68a0a14fd76659d3d4a5f_720w.png

· 每个订单会标明需要什么材料,比如灰色的是蘑菇,红色的代表西红柿,棕色的是洋葱(吗),等等。

· 每个订单也简单标注着自己的制作流程,比如先把切碎西红柿,接着放在灶台上煮,当煮好后,将菜盛到盘子里递给顾客。

现在,我们已经学会了怎么玩分手厨房啦~

要怎么样才进入下一关呢?

每一关有积分要求。制作完成的订单越多,得分越多,也就可以通关了。

那么怎样制作更多的订单呢?

小伙伴要分工明确,并且密切地配合,快马加鞭地做菜。
比如同学小张负责切菜,拿食材,同学小丁负责煮,上菜等等。

很多时候能者还要多劳。

实际操作,就会发现简单的规则,但是操作和配合却很难控制。

长时间不能完成的订单就会销毁(可能顾客等不及退单了),然后被扣分。

食物煮的太久就会着火,着火就要救火。

两个人走位不对就会撞在一起,而且容易冲突起来,该拿的食材没有拿。

这些困难对应着协程的理解,可实际上,理解协程很简单,因为你只需要明白订单本身就可以啦。

协程/coroutine

协程可以让计算机程序在IO密集型的场景下,支持更多的请求,而且比多线程的方式,节省更多地资源,性能更优。

那什么是协程呢? 维基百科的定义是——

协程(英语:coroutine)是计算机程序的一类组件,推广了协作式多任务的子程序,允许执行被挂起与被恢复。

定义里面的关键词是协作式挂起恢复

是不是有点难理解?实际上它们就真真切切地存在分手厨房里面:

· 协作式,玩家们不停地协作制作订单~

· 挂起,当食材切好,放在锅里煮着的时候,这个订单就被“挂起”了~

· 当菜煮好的时候,你就去盛它,这个订单就被恢复了~

但是协程比分手厨房奇妙的地方在于,站在前人的肩膀上,我们不用像游戏里的小人一样忙手忙手,不知所措,只需编写订单,计算机就会充当游戏里的小人们把订单完成得漂漂亮亮的。

这些订单就是我们的代码,但是它们又不同于常见的代码。它们是借助于async, await构造的协程/coroutine,看起来像是同步的代码,但是计算机会用异步的方式执行。

什么是同步的代码,什么又是异步的方式呢?什么是异步的执行用同步编写的代码?

异步是什么?

同步是什么? 举个例子,我们看视频的时候,如果画面和声音不同步,会觉得很别扭。

同步可以理解为多个对象具有相对应的关系。

异步我们生活中用得很少,一般出现在专业领域。比如异步IO,异步通信,英文是asynchronous。简单理解就是不是同步。(<--这不是废话嘛。)

类比分手厨房,订单本身是同步编写的:先切,后煮,上菜。

而订单的实际制作是异步的方式——

· 当出现一个订单的时候,我们有空的时候,就会去拿食材,然后切菜。

· 但是如果其他菜已经做好了,我们就会放下当前的订单,去处理其他的订单。

所以订单们不是从一开始制作,不停地烹饪,直到一个订单完成,才去制作其他订单,而是中间穿插了多个不同订单的制作。

这就是异步方式地烹饪菜肴。

所以异步地执行是指做事情仍按照顺序,但是并不要求顺序在时间上相连,只要按照逻辑的顺序即可。

带有async, await, yield,promise的函数就是协程/coroutine,也就是会异步地执行的代码。这些代码看起来跟同步方式执行的代码没有区别,所以叫用同步编写的代码。

再论协程/Coroutine

现在,让我们以新的论述来理解coroutine——

协程/Coroutine就是函数,可以suspend和resume的函数,也就是可以暂停这个函数的执行 (在suspend的地方直接返回到caller),去做其他事情,然后在实际成熟的时候恢复到离开的位置继续开始运行。

让我们代入线程后看看是怎么实现的,下图左边灰色的coroutine(一个特别点的函数)。

当它被调用的时候,被切成了两个部分,蓝色框框和黄色框框:

· 线程1在执行完成蓝色的框框后,就继续做其他事情了

· 第二部分黄色的框框被suspend了。

当时机成熟的时候,线程2(可以是线程1)就开始执行第二部分黄色的框框,从而完成函数的运行。

实战例子

通过分手厨房,我们可以形象地理解协程了。但是如果要熟练运用,还需要动手实践 。

这里提供一个简单的Javascript代码,也就是上面所说的订单,来感受一下在代码中的协程。

function BoilTheWater(orderNumber, step) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`第${orderNumber}个订单第 ${step} step finished: boiled`);
        }, 5000* Math.random());
    });
}


function CutTheOnion(orderNumber, step) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`第${orderNumber}个订单第 ${step} step finished: cutting`);
        }, 5000);
    });
}

async function ServeTheOrder(orderNumber, step) {
    return `第${orderNumber}个订单第 ${step} step finished: serving order`;
}
async function MakeOnionOrder(i) {
    let res = await CutTheOnion(i, 1);
    console.log(res);
    res = await BoilTheWater(i, 2);
    console.log(res)
    res = await ServeTheOrder(i, 3);
    console.log(res)
}

for (let i=0; i < 10; i++) {
    MakeOnionOrder(i);
}

 

这个代码很简单:有一个洋葱订单,它有三个步骤,首先切,接着煮,最后上菜。接着我们一共制作了十个订单。

从下面的输出我们可以看到,订单完成的时间顺序是交叉的,但是每个订单的步骤都是确定的。

 第0个订单第 1 step finished: cutting
第1个订单第 1 step finished: cutting
第2个订单第 1 step finished: cutting
第3个订单第 1 step finished: cutting
第4个订单第 1 step finished: cutting
第5个订单第 1 step finished: cutting
第6个订单第 1 step finished: cutting
第7个订单第 1 step finished: cutting
第8个订单第 1 step finished: cutting
第9个订单第 1 step finished: cutting
第6个订单第 2 step finished: boiled
第6个订单第 3 step finished: serving order
第1个订单第 2 step finished: boiled
第1个订单第 3 step finished: serving order
第8个订单第 2 step finished: boiled
第8个订单第 3 step finished: serving order
第4个订单第 2 step finished: boiled
第4个订单第 3 step finished: serving order
第9个订单第 2 step finished: boiled
第9个订单第 3 step finished: serving order
第2个订单第 2 step finished: boiled
第2个订单第 3 step finished: serving order
第7个订单第 2 step finished: boiled
第7个订单第 3 step finished: serving order
第5个订单第 2 step finished: boiled
第5个订单第 3 step finished: serving order
第0个订单第 2 step finished: boiled
第0个订单第 3 step finished: serving order
第3个订单第 2 step finished: boiled
第3个订单第 3 step finished: serving order
 

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK