同步机制(上)

进程特性:

并发:

1.进程的执行是间断性的(进程由于调度问题导致可能中间被打断)

2.进程的相对执行速度不可预测

共享:进程/线程之间的制约

不确定性:进程执行的结果与其执行的相对速度有关因此是不确定的

由于并发,进程可能会出现与时间有关的错误

进程get,copy,put并发执行,f s t g为缓冲区,其中s t只能存放一个数据

   假如get进程执行了两次(由于并发是可能发生的),则s中数据就被覆盖掉

假如刚开始就执行put进程,那么g缓冲区中就存了一个空数据

这些都表明了进程执行结果的正确性与时间有关

g1:第一次执行get进程,c1同理,分支表示两者没有先后顺序,但都要执行

只有当这三个进程满足这样的前驱图时,才能保证结果的正确性。

进程互斥:

竞争条件:

spooling技术应用于打印机时会出现这样的场景:

spooler directory保存着要打印文件的文件名,out指明要打印文件的位置in执行文件添加进目录的位置

两个进程A和进程B,首先进程A先运行,A会把所要打印的文件名放入7位置,但是就在A刚想把in递增时,由于进程调度被换下CPU,此时进程B上CPU,也往目录中放要打印文件的文件名,但是此时in所指的7位置处已经存了进程A所要打印文件的文件名,就会把A进程保存的信息覆盖掉,这就出现了问题。

竞争条件:两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序

进程互斥:由于各进程要求使用共享资源(变量、文件等),而这些资源需要排他性使用,导致了各进程之间竞争使用这些资源,这样的关系叫进程互斥

临界资源:系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源或共享变量

临界区:进程中对临界资源操作的程序片段

临界区原则:

1.没有进程在临界区时,想进入临界区的进程可进入
2.不允许两个进程同时处于其临界区中
3.临界区外运行的进程不得阻塞其他进程进入临界区
4.不得使进程无限期等待进入临界区、

ps.图中A进程已进入临界区,A进程在临界区期间,进程B也想进入临界区,此时B会被阻塞直到A离开临界区。

实现进程互斥的方案(软件实现)

实现1:

free: 临界区空闲标志

true: 有进程在临界区

false:无进程在临界区

初值:free为false

但是此方案会出现问题首先假设P进程先执行,free为false,此时while循环跳过,还没重新给free赋值,此时由于进程调度换成进程Q上CPU,free还是false,那么Q进程跳过while循环,进到临界区中,之后再度由于进程调度,P进程上CPU,此时是从free = true开始执行,也进入了临界区,这就不符合临界区使用规则。

解决方法:

问题在于,进临界区前忘记上锁,所以将while(free)和free = true两个语句绑定成原语即可

实现2:

turn: 谁进临界区的标志 true: P进程进临界区 false: Q进程进临界区
turn初值任意

问题:假设turn初始为false,那么P无法进入临界区,假如Q也不想进入临界区的话,那么临界区中始终没有进程,这也违反了规则。

实现3:

pturn, qturn: 初值为false
P进入临界区的条件: pturn∧ not qturn
Q进入临界区的条件: not pturn∧ qturn

问题:

P进程先执行,执行到pturn = true完成,由于进程调度换成Q进程上CPU,最终Q进程因为pturn为true(忙等待:进程死循环换也需要CPU),也进不了临界区,之后切到P也由于qturn为true,也进不了临界区,最终临界区中没有进程。

解决方法:

P进程中pturn = true和while(qturn)合并成原语,Q进程中同理

实现4(DERKKER算法):

在实现3基础上加了turn变量来改进

执行过程:初始假设turn值为1,qturn,pturn都为false,首先假设执行P进程,pturn变为true,qturn为false,跳过while循环进入临界区,之后turn变成2,Q进程执行时,进入循环,但是turn为2,跳过if,进入临界区。

如何解决实现3中的问题?

pturn = true执行完后切换到Q进程,qturn变为true,进入while循环,turn为1,通过if发现turn为1,此时应该p进程进入临界区,而不是q进程,所以把qturn置为false,然以后进入while(turn == 1)一直循环等到时间片用完切换到进程P。由于P进程中qturn为false,跳过while,进入临界区,最终可正确执行。

实现4(PETERSON方法)

进程只要调用函数enter_region和函数leave_region即可

enter_region函数,参数是进程号,假设此时有两个进程要进入临界区,process为0,other为1,把兴趣数组中第0个设成TRUE,表明0进程想进入临界区,turn = 0.如果此时由于进程调度,换成1进程执行,那么turn又变成1,但是此时turn == process&&兴趣数组第一项为TRUE,就一直进行循环直到使用完时间片。切换到0进程,由于turn = 1 不等于 0 所以,最终0进程先进入临界区,实现了先来先进顺序。

进程互斥的硬件实现:

开关中断”指令:

执行“关中断”指令
临界区操作
执行“开中断”指令

优缺点:

简单,高效
代价高,限制CPU并发能力(临界区大小)
不适用于多处理器(禁止中断只能禁止一个处理器)
适用于操作系统本身,不适于用户进程

测试并加锁指令:

enter_region:

复制锁到寄存器并将锁置1
判断寄存器内容是否是零?
若不是零,跳转到enter_region
返回调用者,进入了临界区

leave_region:

在锁中置0
返回调用者

交换指令:

enter_region:

给寄存器中置1 交换寄存器与锁变量的内容 判断寄存器内容是否是零? 若不是零,跳转到enter_region 返回调用者,进入了临界区

leave_region:

在锁中置0
返回调用者

进程同步:

概念:指系统中多个进程中发生的事件存在某种时序关系,需要相互合作,共同完成一项任务。具体地说,一个进程运行到某一点时,要求另一伙伴进程为它提供消息,在未获得消息之前,该进程进入阻塞态,获得消息后被唤醒进入就绪态

生产者和消费者问题:

问题:

1.一个或多个生产者生产某种类型的数据放置在缓冲区中
2.有消费者从缓冲区中取数据,每次取一项
3.只能有一个生产者或消费者对缓冲区进行操作

要解决的问题:
1.当缓冲区已时,生产者不会继续向其中添加数据
2.当缓冲区为时,消费者不会从中移走数据

解决方案:

count为缓冲区中数据数量

问题:

当消费者中if(count == 0)时,若因为进程调度被切换,生产者进程上CPU,到最后执行wake_up,但是此时消费者进程并没有被睡眠,这就会出现问题。

信号量:

概念:一个特殊变量,用于进程间传递信息的一个整数值

定义如下:
struc semaphore
{
  int count;
  queueType queue;
}

对信号量可以实施的操作:初始化PV(P、V分别是荷兰语的test(proberen)和increment(verhogen))

同时P、V操作为原语操作(primitive or atomic action)

P(s)
{
  s.count --;
  if (s.count < 0)
  {
    该进程状态置为阻塞状态;
    将该进程插入相应的等待队列s.queue末尾;
    重新调度;
  }
}
V(s)
{
    s.count ++;
    if (s.count < = 0)//等于0表明之前s.count为-1,说明有进程处于阻塞状态也需要被唤醒,所以也要算上等于0
    {
        唤醒相应等待队列s.queue中等待的一个进程;
        改变其状态为就绪态,并将其插入就绪队列;
    }
}        

用PV操作解决进程互斥基本过程:

分析并发进程的关键活动,划定临界区
设置信号量 mutex,初值为1
在临界区前实施 P(mutex)
在临界区之后实施 V(mutex)

 用PV操作解决生产者消费者问题:

执行过程:生产者进程先检查缓冲区是否已满(P(&empty)看empty是否为0,为0表明缓冲区中没有空位置),然后是信号量减一,目的是为了生产者与消费者不能同时执行还有不能多个生产者往缓冲区中生产数据,之后生产结束,mutex加一和full加一,目的是为了让消费者能执行,消费者执行,先检查缓冲区中是否有数据(P(&full)看full是否为0,为0表明缓冲区中没有数据),然后是信号量减一,目的是为了生产者与消费者不能同时执行还有不能多个消费者者从缓冲区中读数据,之后消费结束,mutex加一和empty加一(消耗完了,空位多出来了),目的是为了让生产者能执行

进阶:

生产者中两个PP位置调换顺序如何?没影响

生产者中两个VV位置调换顺序如何?结果没有影响,但是拉长了临界区(p(mutex)v(mutex)之间),使消费者上CPU时间变晚了。

消费者中两个PP位置调换顺序如何?假如一上来消费者执行,mutex减1,full由于为0,减一为-1,消费者阻塞,然后生产者由于mutex为0也阻塞,最终两者都阻塞,导致了死锁问题

消费者中两个VV位置调换顺序如何?结果没有影响,但是拉长了临界区(p(mutex)v(mutex)之间),使消费者上CPU时间变晚了。

生产后立即插入缓冲区和取出后立即消费有什么影响?结果没有影响,但是拉长了临界区(p(mutex)v(mutex)之间),使下一个进程上CPU时间变晚了。

读者写者问题:

多个进程共享一个数据区,这些进程分为两组:
读者进程:只读数据区中的数据
写者进程:只往数据区写数据

满足条件:

允许多个读者同时执行读操作
不允许多个写者同时操作
不允许读者、写者同时操作

读者执行情况:
1.无其他读者、写者,该读者可以读
2.若已有写者等待,但有其他读者正在读,则该读者也可以读
3.若有写者正在写,该读者必须等待

写者执行情况:
1.无其他读者、写者,该写者可以写
2.若有读者正在读,该写者等待
3.若有其他写者正在写,该写者等待

解决方案1:

void reader(void)
{
    while (TRUE) {
        ……
        P (w);
        读操作
        V(w);
        ……
    }
}

void writer(void)
{
    while (TRUE) {
        ……
        P(w);
        写操作
        V(w);
        ……
    }
}       

但是该方案满足不了许多读者同时读的情况

方案2:

要想满足多个读者可以同时读的情况,新增加一个rc变量,规定只有第一个读者将信号量减1,其他读进程不做操作,这样就可让多个读进程同时执行,又由于w被第一个读进程置为0,写进程无法写,之后rc由于读进程减少而减少,最终为0,此时将w加一。总的来说就是:一旦某个读进程开始读防止写进程写就第一个读进程将w减1,那么接下来其他读进程就可以执行,直到最后一个读完数据的进程结束后,缓冲区没有新内容可读了,w变成1让写进程写。由于多个进程对rc进行了操作,那么rc也就成了临界资源,所以对rc加以保护,在设计rc资源头尾加上另一个信号量mutex的PV操作

作者水平有限,文章肯定有错还请各位指点!!!感谢!!!

时间: 2024-10-10 07:15:38

同步机制(上)的相关文章

游戏中的网络同步机制——Lockstep(帧同步)

本文来自: https://bindog.github.io/blog/2015/03/10/synchronization-in-multiplayer-networked-game-lockstep/#top 值得参考文章:https://blog.codingnow.com/2018/08/lockstep.html 可参考的项目工程:https://github.com/CraneInForest/LockStepSimpleFramework-Shared 0x00 前言 每个人或多或

8天玩转并行开发——第四天 同步机制(上)

在并行计算中,不可避免的会碰到多个任务共享变量,实例,集合.虽然task自带了两个方法:task.ContinueWith()和Task.Factory .ContinueWhenAll()来实现任务串行化,但是这些简单的方法远远不能满足我们实际的开发需要,从.net 4.0开始,类库给我们提供了很多 的类来帮助我们简化并行计算中复杂的数据同步问题. 大体上分为二种: ①   并发集合类:           这个在先前的文章中也用到了,他们的出现不再让我们过多的关注同步细节. ②  轻量级同步

[内核同步]浅析Linux内核同步机制

转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral 很早之前就接触过同步这个概念了,但是一直都很模糊,没有深入地学习了解过,近期有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这两本书的相关章节.趁刚看完,就把相关的内容总结一下.为了弄清楚什么事同步机制,必须要弄明白以下三个问题: 什么是互

配置的同步机制是如何实现的?

配置的同步机制是如何实现的? 配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置:第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置.要了解配置同步机制的实现原理,先得从认识一个名为ConfigurationReloadToken的类型开始. [ 本文已经同步到<ASP.NET Core框架揭秘>之中] 目录一.从ConfigurationReloadToken说起二.Configuration对象与配置文件的同步三.应用重新加载的配置四.同

Linux 内核的同步机制,第 1 部分 + 第二部分(转)

http://blog.csdn.net/jk198310/article/details/9264721  原文地址: Linux 内核的同步机制,第 1 部分 一. 引言 在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问.尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问.在主流的Linux内核中包含了几乎所有现代的操作系统具有的同步机制,这些同步机制包括:原子操作

Java多线程同步机制

Java的多线程同步机制和其他语言开发的是一样的,在当前线程中,遇到某个事件时,等待另一个线程运行结束或者另一个线程的事件,然后再决定如何处理. 本例来自书上的实例,精简了代码,调整了部分逻辑,使得看起来更加简洁明了.已经运行通过. 代码如下: package SwingExample; import java.awt.BorderLayout; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPro

浅谈利用同步机制解决Java中的线程安全问题

我们知道大多数程序都不会是单线程程序,单线程程序的功能非常有限,我们假设一下所有的程序都是单线程程序,那么会带来怎样的结果呢?假如淘宝是单线程程序,一直都只能一个一个用户去访问,你要在网上买东西还得等着前面千百万人挑选购买,最后心仪的商品下架或者售空......假如饿了吗是单线程程序,那么一个用户得等前面全国千万个用户点完之后才能进行点餐,那饿了吗就该倒闭了不是吗?以上两个简单的例子,就说明一个程序能进行多线程并发访问的重要性,今天就让我们去了解一下Java中多线程并发访问这个方向吧. **第一

Fastdfs分布式文件系统之文件同步机制

在前面几篇文章中我们对fastdfs系统的概述.tracker server.storage server以及文件的上传.下载.删除等功能的介绍, 本文将对同一组的不同storage server之间的同步以及新增storage server的同步进行介绍. fastdfs文件系统结构 fastdfs文件系统原理 从fastdfs文件系统结构中我们可以看出不管是上传文件.删除文件.修改文件及新增storager server,文件的同步都是同组 内多台storager server之间进行的.下

【总结】Java线程同步机制深刻阐述

原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread)是一份独立运行的程序,有自己专用的运行栈.线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等. 当多个线程同时读写同一份共享资源的时候,可能会引起冲突.这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团. 同步这个词是从英文synchronize

Linux内核同步机制

http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环境来提高操作系统效率.首先,看看我们最熟悉的两种机制——信号量.锁. 一.信号量 首先还是看看内核中是怎么实现的,内核中用struct semaphore数据结构表示信号量(<linux/semphone.h>中): [cpp] view plaincopyprint? struct semaph