yield()函数的使用

来源:互联网 发布:windows basic配色方案 编辑:程序博客网 时间:2024/05/19 01:08

yield函数在并发编程中是非常重要的一个概念,尤其在多核计算机还不是那么普及的年代,要完成一个“并发”程序,是不得不借助yield函数的。
我们知道,yield的意思是“屈服、礼让”,在程序中表现为当前线程会尽量让出CPU资源来给其他线程执行,但是yield函数背后究竟发生了什么呢,我们结合线程、栈来说明函数背后是如何执行,程序是如何配合CPU来完成yield函数的。

首先我们来看一个例子,

100:A(){    B();    104:...}200:B(){    yield();    204:...}300:C(){    D();    304:...}400:D(){    yield();    404:...yeald(){    找到 ?;    jump ?;}}

上面一段代码,整体说明:A(),B()为一个线程在执行,C(),D()为另一个线程在执行,在执行到B()函数的时候,调用yield从a线程跳到b线程执行,执行到D()函数的时候,又调用yield返回a线程继续执行。
如果我们这样设计行不行呢?

两个线程公用一个栈:pc指到100,a线程进入A()函数开始执行,进入B()函数之前将指针104压栈,B函数刚执行就遇到yield函数,将204压栈,a线程停止执行,yield给b线程开始执行,进入C函数,将304压栈后进入D(),进入D遇到yield函数则将404压栈。注意,此时yield函数我们的本意是希望调回到a线程仅需执行204,此时弹栈,弹出404,这就不对了,我们不是要执行204吗?
可以看到两个线程公用一个栈,使用yield函数是无法完成我们想要的效果的。设计者们意识其实线程在进程中的模型和进程在操作系统中的模型在某些方面是由一些相似的,一个线程应该对应一个栈与其他线程隔离就像一个进程对应一块隔离的内存区域一样,如果这样当我们从a线程跳到b线程执行的时候,如果需要跳回来则CPU需要在跳过去的时候记录a线程的相关信息执行完b线程后才能通过相关信息再跳回a线程来执行,这不是和我们学习过的PCB(process control block)非常相似吗?

多个线程维护各自的栈,这是一个非常伟大的过渡,让很多线程操作都变得清晰起来。

yield(){    TCB2.esp = esp;    esp = TCB1.esp;}

上面这个yield的函数才是真正从b线程跳回a线程执行的操作,我们通过栈操作来屡清执行的顺序。Thread control block 线程控制块,可以理解为由进程来进行管理的一种数据结构,这个数据结构中有线程的相关信息,其中一个字段为esp,它是当前线程的栈指针。回到上面的函数中,esp是CPU寄存器的指针指向的位置,可以看到,yield函数其实就是把当前CPU执行的线程的栈保存起来,并且取出需要执行的线程的栈指针,赋给寄存器开始执行。
现在就显而易见了,当a线程跳到b线程的时候,a线程的栈中104 204先后压栈,跳到b线程后304 405先后压入当前线程的栈中,又通过切换寄存器的栈指针跳回到a线程中来执行,此时弹栈,继续执行204,一切是那么完美!
尔后,每个线程中的地址和变量不断出入栈、线程不断的切来切去,这样就完成了我们设计的“并发”程序!

0 0