急急急!!!pv原语实现爸爸、儿子、女儿三个并发进程的同步

急急急!!!pv原语实现爸爸、儿子、女儿三个并发进程的同步,第1张

桌上有一空盘,只允许存放一个水果。爸爸可向盘中放苹果,也可向盘中放桔子。儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘中空时一次只能放一只水果供吃者取用,请用P、V原语实现爸爸、儿子、女儿三个并发进程的同步。
分析 在本题中,爸爸、儿子、女儿共用一个盘子,且盘中一次只能放一个水果。当盘子为空时,爸爸可将一个水果放入果盘中。若放入果盘中的是苹果,则允许女儿吃,儿子必须等待;若放入果盘中的是桔子,则允许儿子吃,女儿必须等待。本题实际上是生产者-消费者问题的一种变形。这里,生产者放入缓冲区的产品有两类,消费者也有两类,每类消费者只消费其中固定的一类产品。
解 在本题中,应设置三个信号量S、So、Sa,信号量S表示盘子是否为空,其初值为1;信号量So表示盘中是否有桔子,其初值为0;信号量Sa表示盘中是否有苹果,其初值为0。同步描述如下:
int S=1;
int Sa=0;
int So=0;
main(
)
{
cobegin
father();
son();
daughter();
coend
}
father()
{
while(1)
{
P(S );
将水果放入盘中;
if (放入的是桔子) V(So);
else V(Sa);
}
}
son(
)
{
while(1)
{
P(So);
从盘中取出桔子;
V(S);
吃桔子;
}
}
daughter( )
{
while(1)
{
P(Sa);
从盘中取出苹果;
V(S);
吃苹果;
}
}

用PV *** 作实现进程同步,信号量的初值为0。

PV *** 作属于典型的同步机制之一。用一个信号量与一个消息联系起来,当信号量的值为0时,表示期望的消息尚未产生;当信号量的值非0时,表示期望的消息已经存在。用PV *** 作实现进程同步时,调用P *** 作测试消息是否到达,调用V *** 作发送消息。

扩展资料:

PV *** 作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断发生。原语不能被中断执行,因为原语对变量的 *** 作过程如果被打断,可能会去运行另一个对同一变量的 *** 作过程,从而出现临界段问题。

用PV *** 作来管理共享资源时,首先要确保PV *** 作自身执行的正确性。由于P(S)和V(S)都是在同一个信号量S上 *** 作,为了使得它们在执行时不发生因交叉访问信号量S而可能出现的错误,约定P(S)和V(S)必须是两个不可被中断的过程,即让它们在屏蔽中断下执行。

PV原语通过 *** 作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序。
信号量的概念1965年由著名的荷兰计算机科学家Dijkstra提出,其基本思路是用一种新的变量类型(semaphore)来记录当前可用资源的数量。有两种实现方式:1)semaphore的取值必须大于或等于0。0表示当前已没有空闲资源,而正数表示当前空闲资源的数量;2) semaphore的取值可正可负,负数的绝对值表示正在等待进入临界区的进程个数。
信号量是由 *** 作系统来维护的,用户进程只能通过初始化和两个标准原语(P、V原语)来访问。初始化可指定一个非负整数,即空闲资源总数。
P原语:P是荷兰语Proberen(测试)的首字母。为阻塞原语,负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。 *** 作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;
V原语:V是荷兰语Verhogen(增加)的首字母。为唤醒原语,负责把一个被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息。 *** 作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之。

PV-Engineering为光伏工程,PV=peak voltage为峰值电压。
PV相关:
Page view(综合浏览量): 网站各网页被浏览的总次数一个访客有可能创造十几个甚至更多的 Pageviews。是目前判断网站访问流量最常用的计算方式,也是反映一个网站受欢迎程度的重要指标之一。
PV *** 作
简介 1962年,狄克斯特拉离开数学中心进入位于荷兰南部的艾恩德霍芬技术大学(Eindhoven Technical University)任数学教授。在这里,他参加了X8计算机的开发,设计与实现了具有多道程序运行能力的 *** 作系统——THE Multiprogramming System。 详细资料 THE是艾恩德霍芬技术大学的荷兰文Tchnische Hoogeschool Eindhov –en
PV原语
概述 PV原语通过 *** 作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序。信号量的概念1965年由著名的荷兰计算机科学家Dijkstra提出,其基本思路是用一种新的变量类型(semaphore)来记录当前可用资源的数量。有两种实现方式:1)semaphore的取值必须大于或等于0。0表示当前已没有空闲资源,而正数表示当前空闲资源的数量;2) semaphore的取值可正可负
pV=nRT
概述 克拉伯龙方程式通常用下式表示:PV=nRT……①P表示压强、V表示气体体积、n表示物质的量、T表示绝对温度、R表示气体常数。所有气体R值均相同。如果压强、温度和体积都采用国际单位(SI),R=8314帕·米3/摩尔·K。如果压强为大气压,体积为升,则R=00814大气压·升/摩尔·K。因为n=m/M、ρ=m/v(n—物质的量,m—物质的质量,M—物质的摩尔质量,数值上等于物质的分子量,ρ
提高网站PV
创建门户类网站伊始,如何培养一个可观的PV?? pv 者, pageview也,即页面浏览量,或点击量;通常是衡量一个网络新闻频道或站点甚至一条网络新闻“好坏”的主要指标;当然,有时,还会同时考察另外一个指标,即uv,或unique visitors,指访问某个站点或点击某条新闻的不同IP地址的人数。 pv之于网站,就像收视率之于电视,从某种程度上已成为投资者衡量商业网站表现的最重要尺度。从长远看
传奇终结者变种PV
概述: “传奇终结者变种PV(TrojanPSWLMirpv)”病毒:警惕程度★★★,木马病毒,通过网络传播,依赖系统:WIN9X/NT/2000/XP。 运行后病毒把自己复制到系统目录下,文件名为“Sosexe”,修改注册表实现开机自启动。可终止多种反病毒软件运行,如KV2004、天网防火墙个人版、天网防火墙企业版等等。病毒会频繁查找“传奇客户端”,当用户登陆游戏的时候,试图窃取帐户
Win32TrojDownloaderpv
病毒别名 : 处理时间:2006-08-21 威胁级别:★ 中文名称: 病毒类型:木马 影响系统:Win 9x/ME,Win 2000/NT,Win XP,Win 2003 病毒行为 : 该病毒为windows平台下载其它盗号木马、后门程序等病毒的病毒,病毒运行后将自身复制为伪系统正常文件以迷惑用户,并通过注入技术将病毒关键代码注入系统正常程序进程中以穿过网络防火墙下载其它病毒。病毒主要通过网络欺
false

源起

小飞是一名刚入行前端不久的新人,因为进到了某个大公司,俨然成为了学弟学妹眼中'大神',大家遇到js问题都喜欢问他,这不,此时他的qqd出了这样一条消息

"hi,大神在吗?我有个问题想问,现在我们的代码里面有这样的东西,可是得不到正确的返回结果

1234567function getDataByAjax () {return $ajax(postParam)}var data = getDataByAjax()if (data) {   consolelog(datainfo)}

"哦,你这里是异步调用,不能直接获得返回值,你要把if语句写到回调函数中",小飞不假思索的说到,对于一个‘专业’的fe来说,这根本不是一个问题。
“可是我希望只是改造getDataByAjax这个方法,让后面的代码成立。”
“研究这个没有意义,异步是js的精髓,同步的话会阻塞js调用,超级慢的,但是你要一再坚持的话,用async:true就好了”
“不愧是大神,我回去立刻试一试,么么哒”

两天后,她哭丧着脸登上了qq
“试了一下你的方法,但是根本行不通,哭~~”
“别急,我看看你这个postParam的参数行吗”

123456{   dataType: 'jsonp',async: true}

"这是一个jsonp请求啊,老掉牙的东西了,,jsonp请求是没有办法同步的"
“我知道jsonp请求的原理是通过script标签实现的,但是,你看,script也是支持同步的呀,你看tags/attscriptasyncasp”
“额,那可能是jquery没有实现吧,哈哈”
“大神,你能帮我实现一个jsonp的同步调用方式嘛,拜托了(星星眼)”
虽然他有点奇怪jquery为什么没有实现,但是既然w3school的标准摆在那里,码两行代码又没什么,

1234567891011121314export const loadJsonpSync = (url) => {var result; windowcallback1 = (data) => (result = data)let head = windowdocumentgetElementsByTagName('head')[0]let js = windowdocumentcreateElement('script') jssetAttribute('type', 'text/javascript') jssetAttribute('async', 'sync')  // 这句显式声明强调src不是按照异步方式调用的 jssetAttribute('src', url) headappendChild(js)return result}

额,运行起来结果竟然是undefined!w3cshool的文档竟然也不准,还权威呢,我看也不怎么着,小飞暗自想到。

“刚才试了一下,w3school文档上写的有问题,这个异步属性根本就是错的”
“可是我刚还试过一次这个,我确认是好的呀”

12<script src="loop50000 && put('frist')js"></script><script src="put('second')js"></script>

(有兴趣的同学可以实现以下两个js,并且加上async的标签进行尝试。)
“这个,我就搞不清楚了”,小飞讪讪的说到
对方已离线

抽象

关于这个问题,相信不只是小飞,很多人都难以解答。为什么ajax可以做到同步,但jsonp不行,推广到nodejs上,为什么readFile也可以做到同步(readFileSync),但有的库却不行。
(至于script的async选项我们暂时避而不谈,是因为现在的知识维度暂时还不够,但是不要着急,下文中会给出明确的解释)
现在,让我们以计算机科学的角度抽象这个问题:

我们是否可以将异步代码转化为同步代码呢?(ASYNCCALL => SYNCCALL)

既然是抽象问题,那么我们就可以不从工程角度/性能角度/实现语言等等等方面来看(同步比异步效率低下),每增加一个维度,复杂程度将以几何爆炸般增长下去。

首先,我们来明确一点,==在计算机科学领域==同步和异步的定义

同步(英语:Synchronization),指对在一个系统中所发生的事件(event)之间进行协调,在时间上出现一致性与统一化的现象。在系统中进行同步,也被称为及时(in time)、同步化的(synchronous、in sync)。--摘自百度百科
异步的概念和同步相对。即时间不一致,不统一

明确了这一点,我们可以借助甘特图来表示同步和异步

其中t1和t2是同步的,t1和t3是异步的。
答案就在 *** 作系统原理的大学教材上,我们有自旋锁,信号量来解决问题,伪代码如下

1234567891011121314151617spinLock () {// 自旋锁  fork Wait 3000 unlock() //开启一个异步线程,等待三秒后执行解锁动作  loop until unlock // 不断进行空循环直到解锁动作Put ‘unlock’} //pv原语,当信号量为假时立即执行下一步,同时将信号量置真//反之将当前执行栈挂起,置入等待唤醒队列//uv原语,将信号量置为假,并从等待唤醒队列中唤醒一个执行栈Semaphore () {  pv()  fork Wait 3000 uv()  pv()  uv()Put 'unlock'}

很好,至此都可以在 *** 作系统原理的教材上翻到答案。于是我们在此基础上添加约束条件

仅仅依赖于js本身,我们是否可以将异步代码转化为同步代码呢?(ASYNCCALL => SYNCCALL)

论证

带着这个问题,我们翻看一下jquery的源码
src/ajax/xhrjs#L42
可以看出, ajax的同步机制本质上是由XML>123456if (req->IsObject()) {   ASYNC_CALL(read, req, UTF8, fd, &uvbuf, 1, pos);} else {   SYNC_CALL(read, 0, fd, &uvbuf, 1, pos)   argsGetReturnValue()Set(SYNC_RESULT);}

同步的奥妙在于c++的宏定义上,这是一种借由c++来实现的底层同步方式。
观察了这两种最广泛的异步转同步式调用,我们发现均没有采用js来实现。
似乎从现象层面上来看js无法原生支持,但是这还不够,我们探究在js语义下上面的自旋锁/信号量的特性模拟实现(我知道你们一定会嗤之以鼻,==js本身就是单线程的,只是模拟了多线程的特性== 我无比赞同这句话,所以这里用的不是实现,而是特性模拟实现),另外,由于settimeout具有fork相似的异步执行特性,所以我们用setitmeout暂时代替fork

自旋锁

1第一个实现版本

1234567var lock = truesetTimeout(function () {lock = false}, 5000) while(lock);consolelog('unlock')

我们预期在5000ms后执行unlock语句,但是悲剧的是,整个chrome进程僵死掉了。
为了解释清楚这个问题,我们读一下阮一峰老师的event loop模型
event-loophtml
看样子咱们已经清楚的了解了event loop这个js运行顺序的本质(同步执行代码立即执行,异步代码入等待队列),那么,我们可以基于此给出js vm的调度实现(eventloop的一种实现),当然,咱们为了解释自旋锁失败只需要模拟异步 *** 作, 同步 *** 作,和循环就好

123456789101112131415161718192021222324//taskQueue:任务队列//runPart:当前正在执行的任务(同步指令集)//instruct: 正在执行的指令 function eventloop (taskQueue) {while(runPart = taskQueueshift()) {while(instruct = runPartshift()) {const { type, act, codePart } = instructswitch(type) {case 'SYNC':         consolelog(act)if (act === 'loop')           runPartunshift({             act: 'loop',             type: 'SYNC'})breakcase 'ASYNC':         taskQueuepush(codePart)break}}}}

然后转化我们的第一个版本自旋锁

1234567891011121314151617181920let taskQueue = [[{act: 'var lock = true', type: 'SYNC'}, //var lock = true{       act: 'setTimeout',       type: 'ASYNC',       codePart: [{act: 'lock = false', type: 'SYNC'}]}, // setTimeout(function () { lock = false }, 5000)/{       act: 'loop',       type: 'SYNC'   },/ // while(lock);{       act: 'consolelog(\'sync\')',       type: 'SYNC'} // consolelog('unlock')]]<em id="__mceDel"> </em>

测试一下,符合evnet loop的定义,然后放开注释,我们成功的让loop block住了整个执行过程,lock = false永远也没有机会执行!!!
(真实的调度机制远比这个复杂的多得多的,有兴趣的可以看看webkit~~~的jscore的实现哈)

知道了原理,我们就来手动的改进这部分代码
2改进的代码

12345678910111213141516var lock = truesetTimeout(function () {lock = false   consolelog('unlock')}, 5000) function sleep() {var i = 5000while(i--);} var foo = () => setTimeout(function () {   sleep()lock && foo()})foo()

这个版本的改进我们对while(true);做了切块的动作,实际上这种技巧被广泛的应用到改善页面体验的方面,所以,有些人因为时序无法预知而抗拒使用settimeout这种想法是错误的!
6996528,

小测验1: 改写eventloop和taskQueue,使它支持改进后的代码

可是,如果把代码最后的foo() 变成 foo() && consolelog('wait5sdo'),
我们的代码依然没有成功,why

注意看我们标红的地方,如果你完成了小测验1,就会得到和这张图一致的顺序

==同步执行的代码片段必然在异步之前。==

所以,无论从理论还是实际出发,我们都不得不承认,在js中,把异步方法改成同步方法这个命题是水月镜花

哦对了,最后还需要解释一下最开始我们埋下的坑, 为什么jsonp中的async没有生效,现在解释起来真的是相当轻松,即documentappendChild的动作是交由dom渲染线程完成的,所谓的async阻塞的是dom的解析,而非js引擎的阻塞。实际上,在async获取资源后,与js引擎的交互依旧是push taskQueue的动作,也就是我们所说的async call

推荐阅读: 关于dom解析请大家参考webkit技术内幕第九章资源加载部分

峰回路转

相信很多新潮的同学已经开始运用切了async/await语法,在下面的语法中,getAjax1和console之间的具有同步的特性

1234async function () {var data = await getAjax1()   consolelog(data)}

讲完了event loop和异步的本质,我们来重新审视一下async/await。
老天,这段代码亲手推翻了==同步执行的代码片段必然在异步之前。== 的黄金定律!
惊不惊喜,意不意外,这在我们的模型里如同三体里的质子一样的存在。我们重新审视了一遍上面的模型,实在找不到漏洞,找不到任何可以推翻的点,所以真的必须承认,async/await绝对是一个超级神奇的魔法。
到这里来看我们不得不暂时放弃前面的推论,从async/await本身来看这个问题
相信很多人都会说,async/await是CO的语法糖,CO又是generator/promise的语法糖,好的,那我们不妨去掉这层语法糖,来看看这种代码的本质, 关于CO,读的人太多了,我实在不好老生常谈,可以看看这篇文章,咱们就直接绕过去了,这里给出一个简易的实现
/5800210html

1234567891011121314151617181920function wrap(wait) {var iter iter = wait()const f = () => {const { value } = iternext()   value && valuethen(f)} f()} function wait() {var p = () => new Promise(resolve => {     setTimeout(() => resolve(), 3000)})yield p() consolelog('unlock1')yield p() consolelog('unlock2') consolelog('it\'s sync!!')}

终于,我们发现了问题的关键,如果单纯的看wait生成器(注意,不是普通的函数),是不是觉得非常眼熟。这就是我们最开始提出的spinlock伪代码!!!
这个已经被我们完完全全的否定过了,js不可能存在自旋锁,事出反常必有妖,是的,yield和就是表演async/await魔法的妖精。
generator和yield字面上含义。Gennerator叫做生成器,yield这块ruby,python,js等各种语言界争议很大,但是大多数人对于‘让权’这个概念是认同的(以前看到过maillist上面的争论,但是具体的内容已经找不到了)

扩展阅读---ruby元编程 闭包章节yield(ruby语义下的yield)

所谓让权,是指cpu在执行时让出使用权利, *** 作系统的角度来看就是‘挂起’原语,在eventloop的语义下,似乎是暂存起当时正在执行的代码块(在我们的eventloop里面对应runPart),然后顺序的执行下一个程序块。
我们可以修改eventloop来实现让权机制

小测验2 修改eventloop使之支持yield原语

至此,通过修改eventloop模型固然可以解决问题,但是,这并不能被称之为魔法。

和谐共存的世界

实际上通过babel,我们可以轻松的降级使用yield,(在es5的世界使用让权的概念!!)
看似不可能的事情,现在,让我们捡起曾经论证过的
==同步执行的代码片段必然在异步之前。== 这个定理,在此基础上进行进行逆否转化

==在异步代码执行之后的代码必然不是同步执行的(异步的)。==

这是一个圈子里人尽皆知的话,但直到现在他才变得有说服力(我们绕了一个好长的圈子)
现在,让我们允许使用callback,不使用generator/yield的情况下完成一个wait generator相同的功能!!!

1234567891011121314151617181920function wait() {const p = () => ({value: new Promise(resolve => setTimeout(() => resolve(), 3000))})let state = {next: () => {       statenext = programPartreturn p()}}function programPart() {     consolelog('unlocked1')     statenext = programPart2return p()}function programPart2() {     consolelog('unlocked2')     consolelog('it\'s sync!!')return {value: void 0}}return state}

太棒了,我们成功的完成了generator到function的转化(虽然成本高昂),同时,这段代码本身也解释清楚了generator的本质,高阶函数,片段生成器,或者直接叫做函数生成器!这和scip上的翻译完全一致,同时拥有自己的状态(有限状态机)

推荐阅读 计算机程序的构造和解释 第一章generator部分
小测验3 实际上我们提供的解决方式存在缺陷,请从作用域角度谈谈

其实,在不知不觉中,我们已经重新发明了计算机科学中大名鼎鼎的CPS变换
Continuation-passing_style

最后的最后,容我向大家介绍一下facebook的CPS自动变换工具--regenerator。他在我们的基础上修正了作用域的缺陷,让generator在es5的世界里自然优雅。我们向facebook脱帽致敬!!egenerator

后记

同步异步 可以说是整个圈子里面最喜欢谈论的问题,但是,谈来谈去,似乎绝大多数变成了所谓的‘约定俗称’,大家意味追求新技术的同时,却并不关心新技术是如何在老技术上传承发展的,知其然而不知其所以然,人云亦云的写着似是而非的js。

==技术,不应该浮躁==

PS: 最大的功劳不是CO,也不是babel。regenerator的出现比babel早几个月,而且最初的实现是基于esprima/recast的,关于resprima/recast,国内似乎了解的并不多,其实在babel刚刚诞生之际, esprima/esprima-fb/acron 以及recast/jstransfrom/babel-generator几大族系围绕着react产生过一场激烈的斗争,或许将来的某一天,我会再从实现细节上谈一谈为什么babel笑到了最后~~~~


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/13353323.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-07-20
下一篇 2023-07-20

发表评论

登录后才能评论

评论列表(0条)

保存