Lamport面包店算法
2016-09-28 13:14
211 查看
Lamport面包店算法是解决多个线程并发访问一个共享的单用户资源的互斥问题的算法。
由Leslie Lamport发明[1]。
[隐藏]
1 算法
1.1 类比
1.2 进入临界区
1.3 非临界区
2 算法实现
2.1 定义
2.2 伪代码
2.3 讨论
3 参见
4 外部链接
5 参考文献
Lamport把这个并发控制算法可以非常直观地类比为顾客去面包店采购。面包店只能接待一位顾客的采购。已知有n位顾客要进入面包店采购,安排他们按照次序在前台登记一个签到号码。该签到号码逐次加1。根据签到号码的由小到大的顺序依次入店购货。完成购买的顾客在前台把其签到号码归0. 如果完成购买的顾客要再次进店购买,就必须重新排队。
这个类比中的顾客就相当于线程,而入店购货就是进入临界区独占访问该共享资源。由于计算机实现的特点,存在两个线程获得相同的签到号码的情况,这是因为两个线程几乎同时申请排队的签到号码,读取已经发出去的签到号码情况,这两个线程读到的数据是完全一样的,然后各自在读到的数据上找到最大值,再加1作为自己的排队签到号码。为此,该算法规定如果两个线程的排队签到号码相等,则线程id号较小的具有优先权。
已经拿到排队签到号码的线程,要轮询检查自己是否可以进入临界区。即检查n个线程中,自己是否具有最小的非0排队签到号码;或者自己是具有最小的非0排队签到号码的线程中,id号最小的。
可以用伪代码表示上述检查:
等价于:
一旦线程在临界区执行完毕,需要把自己的排队签到号码置为0,表示处于非临界区.
数组Entering[i]为真,表示进程i正在获取它的排队登记号;
数组Number[i]的值,是进程i的当前排队登记号。如果值为0,表示进程i未参加排队,不想获得该资源。规定这个数组元素的取值没有上界。
正在访问临界区的进程如果失败,规定它进入非临界区,Number[i]的值置0,即不影响其它进程访问这个互斥资源。
每个线程只写它自己的Entering[i]、Number[i],只读取其它线程的这两个数据项。
这个算法不需要基于硬件的原子(atomic)操作实现,即它可以纯软件实现。
使用Entering数组是必须的。假设不使用Entering数组,那么就可能会出现这种情况:设进程i的优先级高于进程j(即
随后进程i又获得CPU时间片,它读取到的
corruption)。算法使用了Entering数组变量,使得修改Number数组的元素值变得“原子化”,解决了上述问题。
具体实现时,可以把上述伪代码中的忙等待(busy wait),换成交出线程的执行权,例如
由Leslie Lamport发明[1]。
目录
[隐藏] 1 算法
1.1 类比
1.2 进入临界区
1.3 非临界区
2 算法实现
2.1 定义
2.2 伪代码
2.3 讨论
3 参见
4 外部链接
5 参考文献
算法[编辑]
类比[编辑]
Lamport把这个并发控制算法可以非常直观地类比为顾客去面包店采购。面包店只能接待一位顾客的采购。已知有n位顾客要进入面包店采购,安排他们按照次序在前台登记一个签到号码。该签到号码逐次加1。根据签到号码的由小到大的顺序依次入店购货。完成购买的顾客在前台把其签到号码归0. 如果完成购买的顾客要再次进店购买,就必须重新排队。这个类比中的顾客就相当于线程,而入店购货就是进入临界区独占访问该共享资源。由于计算机实现的特点,存在两个线程获得相同的签到号码的情况,这是因为两个线程几乎同时申请排队的签到号码,读取已经发出去的签到号码情况,这两个线程读到的数据是完全一样的,然后各自在读到的数据上找到最大值,再加1作为自己的排队签到号码。为此,该算法规定如果两个线程的排队签到号码相等,则线程id号较小的具有优先权。
进入临界区[编辑]
已经拿到排队签到号码的线程,要轮询检查自己是否可以进入临界区。即检查n个线程中,自己是否具有最小的非0排队签到号码;或者自己是具有最小的非0排队签到号码的线程中,id号最小的。可以用伪代码表示上述检查:
(a, b) < (c, d)
等价于:
(a < c) or ((a == c) and (b < d))
非临界区[编辑]
一旦线程在临界区执行完毕,需要把自己的排队签到号码置为0,表示处于非临界区.
算法实现[编辑]
定义[编辑]
数组Entering[i]为真,表示进程i正在获取它的排队登记号;数组Number[i]的值,是进程i的当前排队登记号。如果值为0,表示进程i未参加排队,不想获得该资源。规定这个数组元素的取值没有上界。
正在访问临界区的进程如果失败,规定它进入非临界区,Number[i]的值置0,即不影响其它进程访问这个互斥资源。
伪代码[编辑]
<span class="co1" style="color: rgb(102, 102, 102);">// declaration and initial values of global variables</span> Entering<span class="sy4">:</span> array <span class="br0">[</span><span class="nu0">1</span>..<span class="me1">NUM_THREADS</span><span class="br0">]</span> of <span class="kw4">bool</span> <span class="sy1">=</span> <span class="br0">{</span><span class="kw2">false</span><span class="br0">}</span><span class="sy4">;</span> Number<span class="sy4">:</span> array <span class="br0">[</span><span class="nu0">1</span>..<span class="me1">NUM_THREADS</span><span class="br0">]</span> of integer <span class="sy1">=</span> <span class="br0">{</span><span class="nu0">0</span><span class="br0">}</span><span class="sy4">;</span> <span class="nu0">1</span> lock<span class="br0">(</span>integer i<span class="br0">)</span> <span class="br0">{</span> <span class="nu0">2</span> Entering<span class="br0">[</span>i<span class="br0">]</span> <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span> <span class="nu0">3</span> Number<span class="br0">[</span>i<span class="br0">]</span> <span class="sy1">=</span> <span class="nu0">1</span> <span class="sy2">+</span> max<span class="br0">(</span>Number<span class="br0">[</span><span class="nu0">1</span><span class="br0">]</span>, ..., Number<span class="br0">[</span>NUM_THREADS<span class="br0">]</span><span class="br0">)</span><span class="sy4">;</span> <span class="nu0">4</span> Entering<span class="br0">[</span>i<span class="br0">]</span> <span class="sy1">=</span> <span class="kw2">false</span><span class="sy4">;</span> <span class="nu0">5</span> <span class="kw1">for</span> <span class="br0">(</span>j <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span> j <span class="sy1"><=</span> NUM_THREADS<span class="sy4">;</span> j<span class="sy2">++</span><span class="br0">)</span> <span class="br0">{</span> <span class="nu0">6</span> <span class="co1" style="color: rgb(102, 102, 102);">// Wait until thread j receives its number:</span> <span class="nu0">7</span> <span class="kw1">while</span> <span class="br0">(</span>Entering<span class="br0">[</span>j<span class="br0">]</span><span class="br0">)</span> <span class="br0">{</span> <span class="coMULTI" style="font-style: italic;">/* nothing */</span> <span class="br0">}</span> <span class="nu0">8</span> <span class="co1" style="color: rgb(102, 102, 102);">// Wait until all threads with smaller numbers or with the same</span> <span class="nu0">9</span> <span class="co1" style="color: rgb(102, 102, 102);">// number, but with higher priority, finish their work:</span> <span class="nu0">10</span> <span class="kw1">while</span> <span class="br0">(</span><span class="br0">(</span>Number<span class="br0">[</span>j<span class="br0">]</span> <span class="sy3">!</span><span class="sy1">=</span> <span class="nu0">0</span><span class="br0">)</span> <span class="sy3">&&</span> <span class="br0">(</span><span class="br0">(</span>Number<span class="br0">[</span>j<span class="br0">]</span>, j<span class="br0">)</span> <span class="sy1"><</span> <span class="br0">(</span>Number<span class="br0">[</span>i<span class="br0">]</span>, i<span class="br0">)</span><span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span> <span class="coMULTI" style="font-style: italic;">/* nothing */</span> <span class="br0">}</span> <span class="nu0">13</span> <span class="br0">}</span> <span class="nu0">14</span> <span class="br0">}</span> <span class="nu0">15</span> <span class="nu0">16</span> unlock<span class="br0">(</span>integer i<span class="br0">)</span> <span class="br0">{</span> <span class="nu0">17</span> Number<span class="br0">[</span>i<span class="br0">]</span> <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span> <span class="nu0">18</span> <span class="br0">}</span> <span class="nu0">19</span> <span class="nu0">20</span> Thread<span class="br0">(</span>integer i<span class="br0">)</span> <span class="br0">{</span> <span class="nu0">21</span> <span class="kw1">while</span> <span class="br0">(</span><span class="kw2">true</span><span class="br0">)</span> <span class="br0">{</span> <span class="nu0">22</span> lock<span class="br0">(</span>i<span class="br0">)</span><span class="sy4">;</span> <span class="nu0">23</span> <span class="co1" style="color: rgb(102, 102, 102);">// The critical section goes here...</span> <span class="nu0">24</span> unlock<span class="br0">(</span>i<span class="br0">)</span><span class="sy4">;</span> <span class="nu0">25</span> <span class="co1" style="color: rgb(102, 102, 102);">// non-critical section...</span> <span class="nu0">26</span> <span class="br0">}</span> <span class="nu0">27</span> <span class="br0">}</span>
讨论[编辑]
每个线程只写它自己的Entering[i]、Number[i],只读取其它线程的这两个数据项。这个算法不需要基于硬件的原子(atomic)操作实现,即它可以纯软件实现。
使用Entering数组是必须的。假设不使用Entering数组,那么就可能会出现这种情况:设进程i的优先级高于进程j(即
i<j),两个进程获得了相同的排队登记号(Number数组的元素值相等)。进程i在写
Number[i]之前,被优先级低的进程j抢先获得了CPU时间片,这时进程j读取到的
Number[i]为0,因此进程j进入了临界区.
随后进程i又获得CPU时间片,它读取到的
Number[i]与
Number[j]相等,且
i<j,因此进程i也进入了临界区。这样,两个进程同时在临界区内访问,可能会导致数据腐烂(data
corruption)。算法使用了Entering数组变量,使得修改Number数组的元素值变得“原子化”,解决了上述问题。
具体实现时,可以把上述伪代码中的忙等待(busy wait),换成交出线程的执行权,例如
yield操作.
相关文章推荐
- Python3写爬虫(四)多线程实现数据爬取
- 应用领航:盘点那些年我们一起追过的OS
- 无奇不有!盘点各国自己开发的操作系统
- C#实现多线程的同步方法实例分析
- 浅谈chuck-lua中的多线程
- 可自定义oem的萝卜家园 Ghost XP 新春装机版 V200801 下载
- C#简单多线程同步和优先权用法实例
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- C#多线程编程中的锁系统(三)
- 解析C#多线程编程中异步多线程的实现及线程池的使用
- C#多线程学习之(六)互斥对象用法实例
- 基于一个应用程序多线程误用的分析详解
- C#实现判断操作系统是否为Win8以上版本
- C#多线程学习之(三)生产者和消费者用法分析
- C#多线程学习之(一)多线程的相关概念分析
- C#多线程之Thread中Thread.IsAlive属性用法分析
- 分享我在工作中遇到的多线程下导致RCW无法释放的问题
- C#多线程编程之使用ReaderWriterLock类实现多用户读与单用户写同步的方法
- C#多线程传递参数及任务用法示例
- C#控制台下测试多线程的方法