【JavaScript】吃饱了撑的系列之JavaScript模拟多线程并发
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="cksk1-0-0"><strong><span style="font-family: "Microsoft YaHei"; font-size: 18px" data-offset-key="cksk1-0-0">前言</span></strong></div><div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="ailrt-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ailrt-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="ailrt-0-0">最近,明学是一个火热的话题,而我,却也想当那么一回明学家,那就是,<strong>把JavaScript和多线程并发这两个八竿子打不找的东西,给硬凑了起来</strong>,还写了一个并发库<span data-offset-key="ailrt-1-0"><span data-text="true">concurrent-thread-js</span></span><span data-offset-key="ailrt-2-0">。尴尬的是,当我发现其中的不合理之处,即这个东东的应用场景究竟是什么时,我发现我已经把代码写完了。</span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ailrt-0-0"> </div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ailrt-0-0"> </div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ailrt-0-0"> </div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ailrt-0-0"><span style="color: rgba(255, 0, 0, 1); font-size: 18px"><strong><span style="font-family: "Microsoft YaHei"" data-offset-key="ailrt-0-0"><span data-offset-key="ailrt-2-0"><strong><span data-offset-key="ailrt-0-0"><span data-offset-key="ailrt-2-0">⚠️</span></span></strong>注意! 本文中的线程指的都是用JS异步函数模拟的“假线程”,不是真正意义上的多线程,请不要误解⚠️</span></span></strong></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ailrt-0-0"> </div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ailrt-0-0"> </div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ailrt-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="ailrt-0-0"><span data-offset-key="ailrt-2-0">github地址</span></span></h2>
<p><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="ailrt-0-0"><span data-offset-key="ailrt-2-0">https://github.com/penghuwan/concurrent-thread.js<br></span></span></p>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="77bc8-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 18px" data-offset-key="77bc8-0-0">本文的目的</span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="5roo9-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="5roo9-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="5roo9-0-0">事实上,这个库用处很小,但是在写的过程中,我对Promise,Async函数以及event事件流的使用产生了新的认识,同时也逐渐去学习和了解怎么去从零开始去写一个非业务的,通用的npm模块,所以希望拿出来和大家分享一下,这才是本文的真正的目的。</span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="5roo9-0-0"> </div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="j9r4-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="j9r4-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="j9r4-0-0">好,我们从一个故事开始。</span></div>
</div>
<div class="Image-resizerContainer" data-size="normal">
<div>
<div class="Image-captionContainer" data-size="normal">
<div><span style="font-family: "Microsoft YaHei"; font-size: 16px"><img class="Image FocusPlugin--unfocused Image--isBlock" src="https://pic1.zhimg.com/v2-024c0eb60e96a7178d7eacdfc5553924_b.png" alt="" data-size="normal" data-rawwidth="432" data-rawheight="406" data-watermark="watermark" data-original-src="https://pic1.zhimg.com/v2-024c0eb60e96a7178d7eacdfc5553924_b.jpg" data-watermark-src="https://pic2.zhimg.com/v2-6d4e26e45d2e17903cd5158f191c4e4d_b.jpg"></span></div>
<strong><span style="font-family: "Microsoft YaHei"; font-size: 16px">场景一</span></strong></div>
</div>
</div>
<div class="Image-resizerContainer" data-size="normal">
<div>
<div class="Image-captionContainer" data-size="normal">
<div><span style="font-family: "Microsoft YaHei"; font-size: 16px"><img class="Image FocusPlugin--unfocused Image--isBlock" src="https://pic2.zhimg.com/v2-b539cf84592b7dcdd07e7cbaa52f1a8d_b.png" alt="" data-size="normal" data-rawwidth="432" data-rawheight="406" data-watermark="watermark" data-original-src="https://pic2.zhimg.com/v2-b539cf84592b7dcdd07e7cbaa52f1a8d_b.jpg" data-watermark-src="https://pic4.zhimg.com/v2-107559786c533e54aeb70463ba2c9aab_b.jpg"></span></div>
<strong><span style="font-family: "Microsoft YaHei"; font-size: 16px">场景二</span></strong></div>
<div class="Image-captionContainer" data-size="normal"> </div>
</div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="a49j9-0-0">
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="a49j9-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 18px" data-offset-key="a49j9-0-0"><span data-text="true">github地址</span></span></h2>
</div>
<div class="LinkCard FocusPlugin--unfocused LinkCard--noImage" data-draft-node="block" data-draft-type="link-card" data-offset-key="f2uee-0-0">
<div class="cnblogs_code">
<pre>https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">github.com/penghuwan/concurrent-thread.jsgithub.com</span></pre>
</div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="2u0l4-0-0"><span style="color: rgba(255, 0, 0, 1)"><strong><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="2u0l4-0-0"><span data-text="true">注意!倘若不考虑webworker这种解决方案,我们一般都认为JS是单线程的。</span></span></strong></span></div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="3e0st-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="3e0st-0-0"><span data-text="true">concurrent-thread-js功能简介</span></span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="8vtvt-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8vtvt-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="8vtvt-0-0"><span data-text="true">为单线程的JavaScript实现并发协调的功能,语意,命名和作用性质上参考Java的实现,提sleep/join/interupt等API以及锁和条件变量等内容,并提供线程间通信的功能,依赖ES6语法,基于Promise和Async函数实现,故需要Babel编译才能运行。JavaScrpt本来就是单线程的,所以这只是在API的层面实现了模拟,<span data-offset-key="8vtvt-0-1"><span data-text="true">在下文的介绍中,每条所谓的线程其实就是普通的异步函数,并在此基础上实现不同线程的协调配合。</span></span></span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8vtvt-0-0"> </div>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="efalq-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="efalq-0-0"><span data-text="true">为什么不选用webworker实现?</span></span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="cj5gb-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="cj5gb-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="cj5gb-0-0"><span data-text="true">没错,一般来说JS中模拟多线程我们也许会选用webworker,但是它必须要求你手动创建额外的webworker脚本文件,并通过new work('work.js')这种方式使用,这并不能达到我项目中想要的API的效果,而且注意:webwork中的环境不是window!很多方法你调用不了的。你只能采取这种方案,也即在主线程完成该功能,这是我没有选择webworker的另一个原因。</span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="btghd-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="btghd-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="btghd-0-0"><span data-text="true">说是这样说,但其实在大多数时候还是用webworker就够了</span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="btghd-0-0"> </div>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="3da2i-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="3da2i-0-0"><span data-text="true">什么时候使用concurrent-thread-js</span></span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="7280j-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="7280j-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="7280j-0-0"><span data-text="true">这个问题真是灵魂拷问,可是既然代码写都写了,我怎么也得编一个理由出来啊!额。。。让我想想哈</span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="8h7dd-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8h7dd-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="8h7dd-0-0"><span data-text="true">它的作用是<span data-offset-key="8h7dd-0-1"><span data-text="true">:当JS工程需要让两个函数在执行上不互相干扰,同时也不希望它们会阻塞主线程,与此同时,还希望这两个函数实现类似并发多线程之间的协调的需求的时候,你可以使用这个并发模拟库,<span data-offset-key="8h7dd-0-2"><span data-text="true">实际上这种应用场景。。。这尼玛有这种应用场景吗?!(扎心了呀)<span data-offset-key="8h7dd-0-3"><span data-text="true">。</span></span></span></span></span></span></span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8h7dd-0-0"> </div>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="chkhk-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="chkhk-0-0"><span data-text="true">API总览</span></span></h2>
<ul class="public-DraftStyleDefault-ul" data-offset-key="3789s-0-0">
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-reset public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="3789s-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="3789s-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="3789s-0-0"><span data-text="true">submit(function,)<span data-offset-key="3789s-0-1"><span data-text="true">: 接收一个函数,普通函数或Async函数均可,并异步执行"线程"</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="5i4pd-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="5i4pd-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="5i4pd-0-0"><span data-text="true">sleep(ms)<span data-offset-key="5i4pd-0-1"><span data-text="true">: "线程"休眠,可指定休眠时间ms,以毫秒计算</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="fp4t7-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="fp4t7-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="fp4t7-0-0"><span data-text="true">join(threadName)<span data-offset-key="fp4t7-0-1"><span data-text="true">: "线程"同步,调用此方法的"线程"函数将在threadName执行结束后继续执行</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="bv3si-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="bv3si-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="bv3si-0-0"><span data-text="true">interupt(threadName)<span data-offset-key="bv3si-0-1"><span data-text="true">: "线程"中断,影响"线程"内部调this.isInterrupted()的返回值</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="bnr21-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="bnr21-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="bnr21-0-0"><span data-text="true">Lock.lock<span data-offset-key="bnr21-0-1"><span data-text="true">: 加锁,一个时刻只能有一个"线程"函数进入临界区,其他"线程"函数需要等待,锁是非公平的,也就是说后面排队的线程函数没有先后,以随机的方式进行竞争。</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="5ssvg-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="5ssvg-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="5ssvg-0-0"><span data-text="true">Lock.unlock<span data-offset-key="5ssvg-0-1"><span data-text="true">:解除非公平锁</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="89t7e-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="89t7e-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="89t7e-0-0"><span data-text="true">Condition.wait<span data-offset-key="89t7e-0-1"><span data-text="true">:不具备执行条件,"线程"进入waiting状态,等待被唤醒</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="2k5b7-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="2k5b7-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="2k5b7-0-0"><span data-text="true">Condition.notify<span data-offset-key="2k5b7-0-1"><span data-text="true">:随机唤醒一个wait的"线程"</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="1k8et-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="1k8et-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="1k8et-0-0"><span data-text="true">Condition.notifyAll<span data-offset-key="1k8et-0-1"><span data-text="true">: 尚未编写,唤醒所有wait的"线程"</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="c35pb-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="c35pb-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="c35pb-0-0"><span data-text="true">getState<span data-offset-key="c35pb-0-1"><span data-text="true">: 还没写完 获取"线程"状态,包括RUNNALE(运行),WAITING(等待),BLOCKED(阻塞),TERMINATED(终止)</span></span></span></span></div>
</li>
</ul>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="cm2dc-0-0"><strong><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="cm2dc-0-0"><span data-text="true">三个类:ThreadPool,Lock和Condition</span></span></strong></div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="7ni76-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="7ni76-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="7ni76-0-0"><span data-text="true">我们的API分别写入三个类中,分别是</span></span></div>
</div>
<ul class="public-DraftStyleDefault-ul" data-offset-key="cgapa-0-0">
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-reset public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="cgapa-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="cgapa-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="cgapa-0-0"><span data-text="true">ThreadPool类<span data-offset-key="cgapa-0-1"><span data-text="true">:包含submit/sleep/join/interrupt/getState方法</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="cn726-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="cn726-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="cn726-0-0"><span data-text="true">Lock类<span data-offset-key="cn726-0-1"><span data-text="true">:包含Lock.lock和Lock.unLock方法</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="5rgo8-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="5rgo8-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="5rgo8-0-0"><span data-text="true">Condition类<span data-offset-key="5rgo8-0-1"><span data-text="true">:包含Condition.wait和Condition.notify方法</span></span></span></span></div>
</li>
</ul>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8njb-0-0"><strong><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="8njb-0-0"><span data-text="true">注:以下所说的"线程"都是指JS中模拟的异步函数</span></span></strong></div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="7jags-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="7jags-0-0"><span data-text="true">A1.submit方法</span></span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="ekken-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ekken-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="ekken-0-0"><span data-text="true">submit模拟提交线程至线程池</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="6e3mv-0-0">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 备注:为循序渐进介绍,以下为简化代码</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 存储每个线程函数的状态,例如是否中断,以及线程状态等</span>
const threadMap =<span style="color: rgba(0, 0, 0, 1)"> {};
class ThreadPool {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟线程中断</span>
<span style="color: rgba(0, 0, 0, 1)"> interrupt(threadName) { }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟线程同步</span>
<span style="color: rgba(0, 0, 0, 1)"> join(threadName, targetThread) { }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟线程休眠</span>
<span style="color: rgba(0, 0, 0, 1)"> sleep(ms) { }
};
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> submit(func, name) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!func <span style="color: rgba(0, 0, 255, 1)">instanceof</span> Function) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 方式1:传入一个具名函数;方式2:传入第二个参数,即线程命名空间</span>
const threadName = func.name ||<span style="color: rgba(0, 0, 0, 1)"> name;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> threadMap负责存储线程状态数据</span>
threadMap = { state: RUNNABLE, isInterrupted: <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)"> };
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 让func异步调用,同时将传入函数的作用域绑定为 ThreadPool原型</span>
<span style="color: rgba(0, 0, 0, 1)"> Promise.resolve({
then: func.bind(ThreadPool.prototype);
})
}</span></pre>
</div>
<p> </p>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="a8uua-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="a8uua-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="a8uua-0-0"><span data-text="true">首先,我们做了三件事情:</span></span></div>
</div>
<ol class="public-DraftStyleDefault-ol" data-offset-key="c6cuu-0-0">
<li class="Editable-styled public-DraftStyleDefault-orderedListItem public-DraftStyleDefault-reset public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="c6cuu-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="c6cuu-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="c6cuu-0-0"><span data-text="true">获取线程函数的命名空间,并初始化线程初始数据,不同线程状态由threadMap全局存储</span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-orderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="a49q3-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="a49q3-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="a49q3-0-0"><span data-text="true">将提交的函数func作为Promise.resolve方法中的一个thenable对象的then参数,这相当于立即"完成"一个Promise,同时在then方法中执行func,<span data-offset-key="a49q3-0-1"><span data-text="true">func会以异步而不是同步的方式进行执行<span data-offset-key="a49q3-0-2"><span data-text="true">,你也可以简单的理解成<span data-offset-key="a49q3-0-3"><span data-text="true">类似于执行了setTimeOut(func,0);</span></span></span></span></span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-orderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="esgu1-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="esgu1-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="esgu1-0-0"><span data-text="true">将func的作用域绑定为新生成的ThreadPool实例,ThreadPool中定义了我们上面我们介绍到的方法,如sleep/join/interupt等,<span data-offset-key="esgu1-0-1"><span data-text="true">这有什么好处呢?这意味着我们可以直接在函数中通过调用this.interrupt的方式去调用我们定义的API了<span data-offset-key="esgu1-0-2"><span data-text="true">,符合我们的使用习惯(注意,class中定义的除箭头函数外的普通函数实际上都存放在原型中)</span></span></span></span></span></span></div>
</li>
</ol>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="9gs9j-0-0">
<div class="cnblogs_code">
<pre>submit(async <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> example() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.interrupt();
});</span></pre>
</div>
<p> </p>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="3cf7m-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="3cf7m-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="3cf7m-0-0"><span data-text="true">但问题在于:现在因为所有的函数通过this调用的都是ThreadPool原型中的方法,我们要在调用唯一的interrupt方法,<span data-offset-key="3cf7m-0-1"><span data-text="true">需要在异步函数中传入"线程"标识,如线程名。这显然不方便,也不优雅,<span data-offset-key="3cf7m-0-2"><span data-text="true">例如下面的命名为example的线程函数</span></span></span></span></span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="bfp94-0-0">
<div class="cnblogs_code">
<pre>submit(async <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> example() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.interrupt('example'<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p> </p>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="2h7n3-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="2h7n3-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="2h7n3-0-0"><span data-text="true">使用这个模块用户会感到奇怪:我明明在example函数中,为什么还要给调用方法传example这个名字参数??难道不能在模块内部把这事情干了吗?</span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="8tbp6-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8tbp6-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="8tbp6-0-0"><span data-text="true">对!我们下面做的就是这件事情,我们编写一个delegateThreadPool方法,由它为ThreadPool代理处理不同“线程“函数的函数名</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="46sdv-0-0">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回代理后的ThreadPool</span>
<span style="color: rgba(0, 0, 255, 1)">function</span> delegateThreadPool(threadName) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> threadName为待定的线程名,在submit方法调用时候传入</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代理后的ThreadPool</span>
const proxyClass =<span style="color: rgba(0, 0, 0, 1)"> {};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取ThreadPool原来的所有的方法,赋给props数组</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> props =<span style="color: rgba(0, 0, 0, 1)"> Object.getOwnPropertyNames(ThreadPool.prototype);
</span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (let prop of props) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代理ThreadPool,为其所有方法增加threadName这个参数</span>
let fnName =<span style="color: rgba(0, 0, 0, 1)"> prop;
proxyClass </span>= (...args) =><span style="color: rgba(0, 0, 0, 1)"> {
const fn </span>=<span style="color: rgba(0, 0, 0, 1)"> baseClass;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> fn(threadName, ...args);
};
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> proxyClass;
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> submit(func, name) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 省略其他代码 。。。</span>
const proxyScope =<span style="color: rgba(0, 0, 0, 1)"> delegateThreadPool(threadName);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 让func异步调用,不阻塞主线程,同时实现并发</span>
<span style="color: rgba(0, 0, 0, 1)"> Promise.resolve({
then: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 给func绑定this为代理后的ThreadPool对象,以便调用方法</span>
<span style="color: rgba(0, 0, 0, 1)"> func.call(proxyScope);
}
});
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用this.sleep方法时,已经无需增加函数命名作为参数了</span>
submit(async <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> example() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.interrupt();
});</span></pre>
</div>
<p> </p>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="d3hua-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="d3hua-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="d3hua-0-0"><span data-text="true">也就是说,我们的线程函数func绑定的已经不是ThreadPool.prototype了,而是delegateThreadPool处理后返回的对象:proxyScope。这时候,我们在“线程”函数体里调用this.interrupt方法时,已经无需增加函数命名作为参数了,因为这个工作,proxyScope对象帮我们做了,其实它的工作很简单——就是它的每个函数,都在一个返回的闭包里面调用ThreadPool的同名函数,并传递线程名作为第一个参数。</span></span></div>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="96l5u-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="96l5u-0-0"><span data-text="true">A2. sleep方法</span></span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="19h0-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="19h0-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="19h0-0-0"><span data-text="true">作用:<span data-offset-key="19h0-0-1"><span data-text="true">线程休眠</span></span></span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="5u17r-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="5u17r-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="5u17r-0-0"><span data-text="true">sleep方法很简单,无非就是返回一个Promise实例,在Promise的函数里面调setTimeOut,等时间到了执行resolve函数,这段时间里修饰Promise的await语句会阻塞一段时间,resolve后又await语句又继续向下执行了,能满足我们想要的休眠效果</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="7dro4-0-0">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟“线程”休眠</span>
<span style="color: rgba(0, 0, 0, 1)">sleep(ms) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (resolve) {
setTimeout(resolve, ms);
})
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提交“线程”</span>
submit(async <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> example() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 阻塞停留3秒,然后才输出1</span>
await <span style="color: rgba(0, 0, 255, 1)">this</span>.sleep(3000<span style="color: rgba(0, 0, 0, 1)">);
console.log(</span>1<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p> </p>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="aqc2v-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="aqc2v-0-0"><span data-text="true">A3. interrupt方法</span></span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="ckluu-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ckluu-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="ckluu-0-0"><span data-text="true">作用:<span data-offset-key="ckluu-0-1"><span data-text="true">线程中断,可用于处理线程停止等操作</span></span></span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="edklv-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="edklv-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="edklv-0-0"><span data-text="true">这里要先介绍一下Java里面的interrupt方法:在JAVA里,你不能通过调用terminate方法停掉一个线程,因为这有可能会因为处理逻辑突然中断而导致数据不一致的问题,所以要通过interrupt方法把一个中断标志位置为true,然后通过isInterrupted方法作为判断条件跳出关键代码。</span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="9b9cj-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="9b9cj-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="9b9cj-0-0"><span data-text="true">所以为了模拟,我在JS中处理“线程”中断也是这么去做的,但是我们这样做的根本原因是:我们压根没有可以停掉一个线程函数的方法!(JAVA是有但是不准用,即废弃了而已)</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8kpdl-0-0">
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟线程中断</span>
<span style="color: rgba(0, 0, 0, 1)"> interrupt(threadName) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!threadName) { <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error('Miss function parameters'<span style="color: rgba(0, 0, 0, 1)">) }
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (threadMap) {
threadMap.isInterrupted </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取线程中断状态</span>
<span style="color: rgba(0, 0, 0, 1)"> isInterrupted(threadName) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!threadName) { <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error('Miss function parameters'<span style="color: rgba(0, 0, 0, 1)">) }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> !!的作用是:将undefined转为false</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> !!<span style="color: rgba(0, 0, 0, 1)">threadMap.isInterrupted;
}</span></pre>
</div>
<p> </p>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="adkib-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="adkib-0-0"><span data-text="true">A4. join方法</span></span></div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="bo24v-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="bo24v-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="bo24v-0-0"><span data-text="true">join(threadName)<span data-offset-key="bo24v-0-1"><span data-text="true">: "线程"同步,调用此方法的"线程"函数将在threadName执行结束后继续执行</span></span></span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="befmr-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="befmr-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="befmr-0-0"><span data-text="true">join方法和上面的sleep方法是一样的道理,我们让它返回一个Promise,只要我们不调resolve,那么外部修饰Promise的await语句就会一直暂停,等到join的那个另一个线程执行完了,我们看准时机!把这个Promise给resolve,这时候外部修饰Promise的await语句不就又可以向下执行了吗?</span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="befmr-0-0"> </div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="befmr-0-0"> </div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="1us6s-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="1us6s-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="1us6s-0-0"><span data-text="true">但问题在于:我们如何实现这个“<span data-offset-key="1us6s-0-1"><span data-text="true">一个函数执行完通知另一个函数的功能呢<span data-offset-key="1us6s-0-2"><span data-text="true">”?没错!<span data-offset-key="1us6s-0-3"><span data-text="true">那就是我们JavaScript最喜欢的套路: 事件流! <span data-offset-key="1us6s-0-4"><span data-text="true">我们下面使用event-emitter这个前后端通用的模块实现事件流。</span></span></span></span></span></span></span></span></span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="647m3-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="647m3-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="647m3-0-0"><span data-text="true">我们只要在任何一个函数结束的时候触发结束事件(join-finished),同时传递该线程的函数名作为参数,然后在join方法内部监听该事件,并在响应时候调用resolve方法不就可以了嘛。</span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="647m3-0-0"> </div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="2mvrg-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="2mvrg-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="2mvrg-0-0"><span data-text="true">首先是在join方法内部监听线程函数的结束事件</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="6onso-0-0">
<div class="cnblogs_code">
<pre>import ee from 'event-emitter'<span style="color: rgba(0, 0, 0, 1)">;
const emitter </span>=<span style="color: rgba(0, 0, 0, 1)"> ee();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟线程同步</span>
<span style="color: rgba(0, 0, 0, 1)">join(threadName, targetThread) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 监听其他线程函数的结束事件</span>
emitter.on('join-finished', (finishThread) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 根据结束线程的线程名finishThread做判断</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (finishThread ===<span style="color: rgba(0, 0, 0, 1)"> targetThread) {
resolve();
}
})
})
}</span></pre>
</div>
<p> </p>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="emdft-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="emdft-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="emdft-0-0"><span data-text="true">同时在线程函数执行结束时触发join-finished事件,传递线程名做参数</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="57ki7-0-0">
<div class="cnblogs_code">
<pre>import ee from 'event-emitter'<span style="color: rgba(0, 0, 0, 1)">;
const emitter </span>=<span style="color: rgba(0, 0, 0, 1)"> ee();
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> submit(func, name) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 0, 0, 1)"> Promise.resolve({
then: func().then(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
emitter.emit(</span>'join-finished'<span style="color: rgba(0, 0, 0, 1)">, threadName);
})
});
}</span></pre>
</div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="c43us-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="c43us-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="c43us-0-0"><span data-text="true">使用如下:</span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="c43us-0-0">
<div class="cnblogs_code">
<pre>submit(async <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> thread1 () {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.join('thread2'<span style="color: rgba(0, 0, 0, 1)">);
console.log(</span>1<span style="color: rgba(0, 0, 0, 1)">);
});
submit(async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> thread2 () {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.sleep(3000<span style="color: rgba(0, 0, 0, 1)">);
console.log(</span>2<span style="color: rgba(0, 0, 0, 1)">)
})
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3s后,依次输出 2 1</span></pre>
</div>
</div>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="6q3qj-0-0"><span class="prism-token token function" style="font-family: "Microsoft YaHei"; font-size: 16px"><span data-offset-key="6q3qj-0-0"><span data-text="true">A5. Lock.lock & Lock.unlock(非公平锁)</span></span></span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="11jk8-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="11jk8-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="11jk8-0-0"><span data-text="true">我们主要是要编写两个方法:lock和unlock方法。我们需要设置一个Boolean属性isLock</span></span></div>
</div>
<ul class="public-DraftStyleDefault-ul" data-offset-key="98qpr-0-0">
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-reset public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="98qpr-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="98qpr-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="98qpr-0-0"><span data-text="true">lock方法:<span data-offset-key="98qpr-0-1"><span data-text="true">lock方法首先会判断isLock是否为false,如果是,则代表没有线程占领临界区,那么允许该线程进入临界区,同时把isLock设置为true,不允许其他线程函数进入。其他线程进入时,由于判断isLock为true,会setTimeOut每隔一段时间递归调用判断isLock是否为false,从而以较低性能消耗的方式模拟while死循环。当它们检测到isLock为false时候,则会进入临界区,同时设置isLock为true。因为后面的线程没有先后顺序,所以这是一个非公平锁</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="6bv31-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="6bv31-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="6bv31-0-0"><span data-text="true">unLock方法:<span data-offset-key="6bv31-0-1"><span data-text="true">unlock则是把isLock属性设置为false,解除锁定就可以了</span></span></span></span></div>
</li>
</ul>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8fqbe-0-0">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这是一个非公平锁</span>
<span style="color: rgba(0, 0, 0, 1)">class Lock {
constructor() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.isLock = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">加锁</span>
<span style="color: rgba(0, 0, 0, 1)"> lock() {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.isLock) {
const self </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 循环while死循环,不停测试isLock是否等于false</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve) =><span style="color: rgba(0, 0, 0, 1)"> {
(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> recursion() {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">self.isLock) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 占用锁</span>
self.isLock = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使外部await语句继续往下执行</span>
<span style="color: rgba(0, 0, 0, 1)"> resolve();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
setTimeout(recursion, </span>100<span style="color: rgba(0, 0, 0, 1)">);
})();
});
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.isLock = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Promise.resolve();
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解锁</span>
<span style="color: rgba(0, 0, 0, 1)"> unLock() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.isLock = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
const lockObj </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Lock();
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> lockObj;</pre>
</div>
<p> </p>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="ck8ce-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ck8ce-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="ck8ce-0-0"><span data-text="true">运行示例如下:</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="bsqsh-0-0">
<div class="cnblogs_code">
<pre>async <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> commonCode() {
await Lock.lock();
await Executor.sleep(</span>3000<span style="color: rgba(0, 0, 0, 1)">);
Lock.unLock();
}
submit(async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> example1() {
console.log(</span>'example1 start'<span style="color: rgba(0, 0, 0, 1)">)
await commonCode();
console.log(</span>'example1 end'<span style="color: rgba(0, 0, 0, 1)">)
});
submit(async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> example2() {
console.log(</span>'example2 start'<span style="color: rgba(0, 0, 0, 1)">)
await commonCode();
console.log(</span>'example2 end'<span style="color: rgba(0, 0, 0, 1)">)
});</span></pre>
</div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="eobkv-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="eobkv-0-0"> </div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="eobkv-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="eobkv-0-0"><span data-text="true">输出</span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="eobkv-0-0">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 立即输出</span>
<span style="color: rgba(0, 0, 0, 1)">example1 start
example2 start
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3秒后输出</span>
<span style="color: rgba(0, 0, 0, 1)">example1 end
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再3秒后输出</span>
example2 end</pre>
</div>
<p> </p>
</div>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="98dma-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="98dma-0-0"><span data-text="true">A6. Condition.wait & Condition.notify(条件变量)</span></span></h2>
<ul class="public-DraftStyleDefault-ul" data-offset-key="fltnu-0-0">
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-reset public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="fltnu-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="fltnu-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="fltnu-0-0"><span data-text="true">Condition.wait<span data-offset-key="fltnu-0-1"><span data-text="true">:不具备执行条件,线程进入waiting状态,等待被唤醒</span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="braut-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="braut-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="braut-0-0"><span data-text="true">Condition.notify<span data-offset-key="braut-0-1"><span data-text="true">: 唤醒线程</span></span></span></span></div>
</li>
</ul>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="8d0af-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="8d0af-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="8d0af-0-0"><span data-text="true">对不起!写到这里,我实在是口干舌燥,写不下去了,但是道理和前面是一样的:</span></span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="egdoj-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="egdoj-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="egdoj-0-0"><span data-text="true">无非是:事件监听 + Promise + Async函数组合拳,一套搞定</span></span></div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="egdoj-0-0">
<div class="cnblogs_code">
<pre>import ee from 'event-emitter'<span style="color: rgba(0, 0, 0, 1)">;
const ev </span>=<span style="color: rgba(0, 0, 0, 1)"> ee();
class Condition {
constructor() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.n = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.list =<span style="color: rgba(0, 0, 0, 1)"> [];
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 当不满足条件时,让线程处于等待状态</span>
<span style="color: rgba(0, 0, 0, 1)"> wait() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve) =><span style="color: rgba(0, 0, 0, 1)"> {
const eventName </span>= `notify-${<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.n}`;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.n++<span style="color: rgba(0, 0, 0, 1)">;
const list </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.list;
list.push(eventName);
ev.on(eventName, () </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从列表中删除事件名</span>
const i =<span style="color: rgba(0, 0, 0, 1)"> list.indexOf(eventName);
list.splice(i, </span>1<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 让外部函数恢复执行</span>
<span style="color: rgba(0, 0, 255, 1)">debugger</span><span style="color: rgba(0, 0, 0, 1)">;
resolve();
})
})
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选择一个线程唤醒</span>
<span style="color: rgba(0, 0, 0, 1)"> notify() {
const list </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.list;
let i </span>= Math.random() * (<span style="color: rgba(0, 0, 255, 1)">this</span>.list.length - 1<span style="color: rgba(0, 0, 0, 1)">);
i </span>=<span style="color: rgba(0, 0, 0, 1)"> Math.floor(i);
ev.emit(list)
}
}</span></pre>
</div>
<p> </p>
</div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="fm8d3-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="fm8d3-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="fm8d3-0-0"><span data-text="true">测试代码</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ac1at-0-0">
<div class="cnblogs_code">
<pre>async <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> testCode() {
console.log(</span>'i will be wait'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
await Condition.wait();
};
console.log(</span>'i was notified '<span style="color: rgba(0, 0, 0, 1)">);
}
submit(async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> example() {
testCode();
setTimeout(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
Condition.notify();
}, </span>3000<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="e4ffl-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="e4ffl-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="e4ffl-0-0"><span data-text="true">输出</span></span></div>
</div>
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="atqr1-0-0">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">i will be wait
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3秒后输出</span>
i was notified</pre>
</div>
<p> </p>
</div>
<h2 class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="42ejl-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="42ejl-0-0"><span data-text="true">最后的大总结</span></span></h2>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="1q8d0-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="1q8d0-0-0"><strong><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="1q8d0-0-0"><span data-text="true">其实说到底,我想和大家分享的不是什么并发啊,什么多线程啦。</span></span></strong></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="32f92-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="32f92-0-0"><strong><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="32f92-0-0"><span data-text="true">其实我想表达的是:事件监听 + Promise + Async函数这套组合拳很好用啊</span></span></strong></div>
</div>
<ul class="public-DraftStyleDefault-ul" data-offset-key="5ov5e-0-0">
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-reset public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="5ov5e-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="5ov5e-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="5ov5e-0-0"><span data-text="true">你想让一段代码停一下?<span data-offset-key="5ov5e-0-1"><span data-text="true">OK!<span data-offset-key="5ov5e-0-2"><span data-text="true">写个返回Promise的函数,用await修饰,<span data-offset-key="5ov5e-0-3"><span data-text="true">它就停啦!</span></span></span></span></span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="ciohf-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ciohf-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="ciohf-0-0"><span data-text="true">你想控制它(await)不要停了,继续往下走?<span data-offset-key="ciohf-0-1"><span data-text="true">OK! <span data-offset-key="ciohf-0-2"><span data-text="true">把Promise给resolve掉,<span data-offset-key="ciohf-0-3"><span data-text="true">它就往下走啦</span></span></span></span></span></span></span></span></div>
</li>
<li class="Editable-styled public-DraftStyleDefault-unorderedListItem public-DraftStyleDefault-depth0 public-DraftStyleDefault-listLTR" data-block="true" data-editor="eig7e" data-offset-key="c0gt4-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="c0gt4-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="c0gt4-0-0"><span data-text="true">你说你不知道怎么控制它停,因为监听和发射事件的代码分布在两个地方?<span data-offset-key="c0gt4-0-1"><span data-text="true">OK!那就使用事件流</span></span></span></span></div>
</li>
</ul>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="fsc7e-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="fsc7e-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="fsc7e-0-0"> </span></div>
</div>
<div class="Editable-unstyled" data-block="true" data-editor="eig7e" data-offset-key="ecmgc-0-0">
<div class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr" data-offset-key="ecmgc-0-0"><span style="font-family: "Microsoft YaHei"; font-size: 16px" data-offset-key="ecmgc-0-0"><span data-text="true">本文完,下面是全部项目代码(刚写了文章才发现有bug,待会改改)</span></span></div>
</div>
</div>
<div id="MySignature" role="contentinfo">
我叫彭湖湾,请叫我胖湾<br><br>
来源:https://www.cnblogs.com/penghuwan/p/11483291.html
頁:
[1]