简介
进程同步是一个操作系统级别的概念,是在多道程序的环境下,存在着不同的制约关系,为了协调这种互相制约的关系,实现资源共享和进程协作,从而避免进程之间的冲突,引入了进程同步。
临界资源
在操作系统中,进程是占有资源的最小单位(线程可以访问其所在进程内的所有资源,但线程本身并不占有资源或仅仅占有一点必须资源)。但对于某些资源来说,其在同一时间只能被一个进程所占用。这些一次只能被一个进程所占用的资源就是所谓的临界资源。典型的临界资源比如物理上的打印机,或是存在硬盘或内存中被多个进程所共享的一些变量和数据等(如果这类资源不被看成临界资源加以保护,那么很有可能造成丢数据的问题)。
对于临界资源的访问,必须是互诉进行。也就是当临界资源被占用时,另一个申请临界资源的进程会被阻塞,直到其所申请的临界资源被释放。而进程内访问临界资源的代码被成为临界区。
对于临界区的访问过程分为四个部分:
1.进入区:查看临界区是否可访问,如果可以访问,则转到步骤二,否则进程会被阻塞
2.临界区:在临界区做操作
3.退出区:清除临界区被占用的标志
4.剩余区:进程与临界区不相关部分的代码
进程间同步和互诉的概念
进程同步
进程同步也是进程之间直接的制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系来源于他们之间的合作。
比如说进程A需要从缓冲区读取进程B产生的信息,当缓冲区为空时,进程B因为读取不到信息而被阻塞。而当进程A产生信息放入缓冲区时,进程B才会被唤醒。概念如图1所示。
1
图1.进程之间的同步
用C#代码模拟进程之间的同步如代码1所示。
class ProcessSyn
{
private static Mutex mut = new Mutex();
static void Main()
{
Console.WriteLine("进程1执行完了进程2才能执行.......");
Thread Thread1 = new Thread(new ThreadStart(Proc1));
Thread Thread2 = new Thread(new ThreadStart(Proc2));
Thread1.Start();
Thread2.Start();
Console.ReadKey();   
}
private static void Proc1()
{
mut.WaitOne();
Console.WriteLine("线程1执行操作....");
Thread.Sleep(3000);
mut.ReleaseMutex();//V操作
}
private static void Proc2()
{
mut.WaitOne();//P操作
Console.WriteLine("线程2执行操作....");
mut.WaitOne();
}
}
代码1.C#模拟进程之间的同步
运行结果如图2所示。
2
图2.运行结果
进程互斥
进程互斥是进程之间的间接制约关系。当一个进程进入临界区使用临界资源时,另一个进程必须等待。只有当使用临界资源的进程退出临界区后,这个进程才会解除阻塞状态。
比如进程B需要访问打印机,但此时进程A占有了打印机,进程B会被阻塞,直到进程A释放了打印机资源,进程B才可以继续执行。概念如图3所示。
3
图3.进程之间的互斥
用C#模拟进程之间的互斥,这里我启动了5个线程,但同一时间内只有一个线程能对临界资源进行访问。如代码2所示。
class ProcessMutex
{
private static Mutex mut = new Mutex();
private const int numThreads = 5;
static void Main()
{
for (int i = 0; i <= numThreads; i++)
{
Thread myThread = new Thread(new ThreadStart(UseResource));
myThread.Name = String.Format("线程{0}", i + 1);
myThread.Start();
}
Console.ReadKey();
}
//同步
private static void UseResource()
{
// 相当于P操作
mut.WaitOne();
/*下面代码是线程真正的工作*/
Console.WriteLine("{0}已经进入临界区",
Thread.CurrentThread.Name);
Random r = new Random();
int rNum = r.Next(2000);
Console.WriteLine("{0}执行操作,执行时间为{1}ms", Thread.CurrentThread.Name,rNum);
Thread.Sleep(rNum);
Console.WriteLine("{0}已经离开临界区\r\n",
Thread.CurrentThread.Name);
/*线程工作结束*/
// 相当于V操作
mut.ReleaseMutex();
}
//互斥
}
代码2.C#模拟进程之间的互斥
运行结果如图4所示。
4   
图4.C#模拟进程互斥
实现临界区互斥的基本方法
硬件实现方法
通过硬件实现临界区最简单的办法就是关CPU的中断。从计算机原理我们知道,CPU进行进程切换是需要通过中断来进行。如果屏蔽了中断那么就可以保证当前进程顺利的将临界区代码执行完,从而实现了互斥。这个办法的步骤就是:屏蔽中断--执行临界区--开中断。但这样做并不好,这大大限制了处理器交替执行任务的能力。并且将关中断的权限交给用户代码,那么如果用户代码屏蔽了中断后不再开,那系统岂不是跪了?
还有硬件的指令实现方式,这个方式和接下来要说的信号量方式如出一辙。但是通过硬件来实现,这里就不细说了。
信号量实现方式
这也是我们比较熟悉P V操作。通过设置一个表示资源个数的信号量S,通过对信号量S的P和V操作来实现进程的的互斥。
P和V操作分别来自荷兰语Passeren和Vrijgeven,分别表示占有和释放。P V操作是操作系统的原语,意味着具有原子性。
P操作首先减少信号量,表示有一个进程将占用或等待资源,然后检测S是否小于0,如果小于0则阻塞,如果大于0则占有资源进行执行。
V操作是和P操作相反的操作,首先增加信号量,表示占用或等待资源的进程减少了1个。然后检测S是否小于0,如果小于0则唤醒等待使用S资源的其它进程。
前面我们C#模拟进程的同步和互斥其实算是信号量进行实现的。
一些经典利用信号量实现同步的问题
生产者--消费者问题
问题描述:生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。本作业要求设计在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来
这里生产者和消费者是既同步又互斥的关系,首先只有生产者生产了,消费着才能消费,这里是同步的关系。但他们对于临界区的访问又是互斥的关系。因此需要三个信号量empty和full用于同步缓冲区,而mut变量用于在访问缓冲区时是互斥的。
利用C#模拟生产者和消费者的关系如代码3所示。
class ProducerAndCustomer
{
//临界区信号量
private static Mutex mut = new Mutex();
private static Semaphore empty = new Semaphore(5, 5);//空闲缓冲区
private static Semaphore full = new Semaphore(0, 5);
//生产者-消费者模拟
static void Main()
{
Console.WriteLine("生产者消费者模拟......");
for (int i = 1; i < 9; i++)
{
Thread Thread1 = new Thread(new ThreadStart(Producer));
Thread Thread2 = new Thread(new ThreadStart(Customer));
Thread1.Name = String.Format("生产者线程{0}", i);
Thread2.Name = String.Format("消费者线程{0}", i);
Thread1.Start();
Thread2.Start();
}
Console.ReadKey();
}
private static void Producer()
{
Console.WriteLine("{0}已经启动",Thread.CurrentThread.Name);
empty.WaitOne();//对empty进行P操作
mut.WaitOne();//对mut进行P操作
Console.WriteLine("{0}放入数据到临界区", Thread.CurrentThread.Name);
Thread.Sleep(1000);
mut.ReleaseMutex();//对mut进行V操作
full.Release();//对full进行V操作
}
private static void Customer()
{
Console.WriteLine("{0}已经启动", Thread.CurrentThread.Name);
Thread.Sleep(12000);
full.WaitOne();//对full进行P操作
mut.WaitOne();//对mut进行P操作
Console.WriteLine("{0}读取临界区", Thread.CurrentThread.Name);
mut.ReleaseMutex();//对mut进行V操作
empty.Release();//对empty进行V操作
}
}
代码3.使用C#模拟生产者和消费者的关系
运行结果如图5所示。
5
图5.生产者消费者C#模拟结果
读者--写者问题
问题描述:
一个数据文件或记录,统称数据对象,可被多个进程共享,其中有些进程只要求读称为"读者",而另一些进程要求写或修改称为"写者"。
规定:允许多个读者同时读一个共享对象,但禁止读者、写者同时访问一个共享对象,也禁止多个写者访问一个共享对象,否则将违反Bernstein并发执行条件。
通过描述可以分析,这里的读者和写者是互斥的,而写者和写者也是互斥的,但读者之间并不互斥。
由此我们可以设置3个变量,一个用来统计读者的数量,另外两个分别用于对读者数量读写的互斥,读者和读者写者和写者的互斥。如代码4所示。
class ReaderAndWriter
{
private static Mutex mut = new Mutex();//用于保护读者数量的互斥信号量
private static Mutex rw = new Mutex();//保证读者写者互斥的信号量
static int count = 0;//读者数量
static void Main()
{
Console.WriteLine("读者写者模拟......");
for (int i = 1; i < 6; i++)
{
Thread Thread1 = new Thread(new ThreadStart(Reader));
Thread1.Name = String.Format("读者线程{0}", i);
Thread1.Start();
}
Thread Thread2 = new Thread(new ThreadStart(writer));
Thread2.Name = String.Format("写者线程");
Thread2.Start();
Console.ReadKey();
}
private static void Reader()
{
mut.WaitOne();
if (count == 0)
{
rw.WaitOne();
}
count++;
mut.ReleaseMutex();
Thread.Sleep(new Random().Next(2000));//读取耗时1S
Console.WriteLine("读取完毕");
mut.WaitOne();
count--;
mut.ReleaseMutex();
if (count == 0)
{
rw.ReleaseMutex();
}
}
private static void writer()
{
rw.WaitOne();
Console.WriteLine("写入数据");
rw.ReleaseMutex();
}
代码4.C#模拟读者和写者问题
运行结果如图6所示。
6
图6.读者写者的运行结果
哲学家进餐问题
问题描述:
有五个哲学家,他们的生活方式是交替地进行思考和进餐。哲学家们公用一张圆桌,周围放有五把椅子,每人坐一把。在圆桌上有五个碗和五根筷子,当一个哲学家思考时,他不与其他人交谈,饥饿时便试图取用其左、右最靠近他的筷子,但他可能一根都拿不到。只有在他拿到两根筷子时,方能进餐,进餐完后,放下筷子又继续思考。
8
图7.哲学家进餐问题
根据问题描述,五个哲学家分别可以看作是五个进程。五只筷子分别看作是五个资源。只有当哲学家分别拥有左右的资源时,才得以进餐。如果不指定规则,当每个哲学家手中只拿了一只筷子时会造成死锁,从而五个哲学家都因为吃不到饭而饿死。因此我们的策略是让哲学家同时拿起两只筷子。因此我们需要对每个资源设置一个信号量,此外,还需要使得哲学家同时拿起两只筷子而设置一个互斥信号量,如代码5所示。
class philosopher
{
private static int[] chopstick=new int[5];//分别代表哲学家的5只筷子
private static Mutex eat = new Mutex();//用于保证哲学家同时拿起两双筷子
static void Main()
{
//初始设置所有筷子可用
for (int k = 1; k <= 5; k++)
{
chopstick[k - 1] = 1;
}
//每个哲学家轮流进餐一次
for(int i=1;i<=5;i++)
{
Thread Thread1 = new Thread(new ThreadStart(Philosophers));
Thread1.Name = i.ToString();
Thread1.Start();
}
Console.ReadKey();
}
private static void Philosophers()
{
//如果筷子不可用,则等待2秒
while (chopstick[int.Parse(Thread.CurrentThread.Name)-1] !=1 || chopstick[(int.Parse(Thread.CurrentThread.Name))%4]!=1)
{
Console.WriteLine("哲学家{0}正在等待", Thread.CurrentThread.Name);
Thread.Sleep(2000);
}
eat.WaitOne();
//同时拿起两双筷子
chopstick[int.Parse(Thread.CurrentThread.Name)-1] = 0;
chopstick[(int.Parse(Thread.CurrentThread.Name)) % 4] = 0;
eat.ReleaseMutex();
Thread.Sleep(1000);
Console.WriteLine("哲学家{0}正在用餐...",Thread.CurrentThread.Name);
//用餐完毕后放下筷子
chopstick[int.Parse(Thread.CurrentThread.Name)-1] = 1;
chopstick[(int.Parse(Thread.CurrentThread.Name)) % 4] = 1;
Console.WriteLine("哲学家{0}用餐完毕,继续思考", Thread.CurrentThread.Name);
}
}
代码5.C#模拟哲学家用餐问题
运行结果如图7所示。
7
图8.哲学家问题运行结果
总结
本文介绍了进程的同步和互斥的概念,临界区的概念,以及实现进程同步互斥的方式,并解决了3种实现同步的经典问题,并给出了相应的C#模拟代码。操作系统对于进程的管理是是计算机编程的基础之一,因此掌握这个概念会使你的内功更上一层:-D
分类: 操作系统原理
本文转自CareySon博客园博客,原文链接:http://www.cnblogs.com/CareySon/archive/2012/04/14/Process-SynAndmutex.html,如需转载请自行联系原作者

浅谈进程同步和互斥的概念相关推荐

  1. HTTP协议漫谈 C#实现图(Graph) C#实现二叉查找树 浅谈进程同步和互斥的概念 C#实现平衡多路查找树(B树)...

    HTTP协议漫谈 简介 园子里已经有不少介绍HTTP的的好文章.对HTTP的一些细节介绍的比较好,所以本篇文章不会对HTTP的细节进行深究,而是从够高和更结构化的角度将HTTP协议的元素进行分类讲解. ...

  2. 浅谈CMMI几个过程概念流程管理 (转)

    浅谈CMMI几个过程概念流程管理 CMMI(Capability Maturity Model Integration)能力成熟度模型集成,正如它的名字一样,它是一个模型.个人觉得它更是一种概念.它带 ...

  3. 浅谈 加签验签 概念

    浅谈 加签验签 概念(一) 我们在求职面试中,经常会被问到,如何设计一个安全对外的接口呢? 其实可以回答这一点,加签和验签,这将让你的接口更加有安全.接下来,本文将和大家一起来学习加签和验签.从理论到 ...

  4. 浅谈SAP CRM开发——技术概念、与ECC 系列产品区别

    SAP CRM首先和SAP ECC,SAP BW一样,都是SAP的一个产品,而不是属于ECC的一个模块,当然CRM在很多功能方面和ECC有重复,比如CRM的SALES和ECC的SD,CRM的SERVI ...

  5. java多线程互斥锁_浅谈Java多线程互斥锁

    为了解决竞争条件带来的问题,我们可以对资源上锁.多个线程共同读写的资源称为共享资源,也叫临界资源.涉及操作临界资源的代码区域称为临界区(Critical Section).同一时刻,只能有一个线程进入 ...

  6. 捷配浅谈PCB叠层的概念、设计原则

    PCB叠层设计,其实和做汉堡有类似的工艺.汉堡店会精心准备每一层汉堡,就像PCB厂家的PCB板层叠在一起一样.汉堡会有不同的大小和形状,有各种各样的配料,秘密酱汁覆盖着我们自己的烤面包.就像我们在PC ...

  7. 【JY】浅谈混凝土结构/构件性能试验指标概念(二)

    因你精彩 即刻关注 ☞ [写在文前] 上期[JY]浅谈混凝土结构/构件性能试验指标概念(一)中从"定量"的角度对混凝土结构/构件的试验指标进行探讨,本期主要从"定性&qu ...

  8. 【JY】浅谈结构设计

    因你精彩 即刻关注 "大道至简,愿工程师们都能做到始知真放本精微" 首先结构是什么呢? 结构是骨架,是建筑的现实意义存在的形式.换句话说,一切的建筑/构筑都有结构,建筑/构筑不同, ...

  9. 云计算浅谈之二:云计算介绍(2)

    本来这一讲应该随上一讲结束,不过本人时间有限,所以拆开了.另外既然题名为浅谈,就些微提一些概念,唤起大家对云计算的注意,抛砖引玉.更多的内容可以参考我上一讲给大家提示的"windows az ...

最新文章

  1. ubuntu中启用ssh服务
  2. Centos查找命令清单
  3. 未老先呆,这锅熬夜真的要背:生物钟影响阿尔茨海默症的机制被发现
  4. php请编写一个函数来将一个_为什么开发人员讨厌PHP
  5. linux中mbr最大多少分区,Linux与磁盘分区介绍(MBR,GPT)
  6. 【开源组件】一份值得收藏的的 MySQL 规范
  7. 把数组排列成最小的数(详解)
  8. 单身狗救星!电子科大校长为理工科男脱单提建议
  9. 《商业智能BI白皮书3.0》正式发布(附下载链接)
  10. pyplot 余弦函数_python如何画出三角函数
  11. php安装ziparchive扩展,记一次PHP扩展-ZipArchive安装
  12. ghs文件可以删除吗_怎么强制删除电脑文件
  13. 如何在云服务器搭建虚拟主机,如何在云服务器搭建虚拟主机
  14. 教程|Word/WPS永久更改默认背景颜色
  15. RFID 有源,半源和无源的区别
  16. Python 爬取新浪网新闻和存取CSV文件
  17. 网页在线视频播放大全
  18. 2022冬-DownKyi 辅助使用的小插件源码分享
  19. Java 常用内置对象
  20. dwz导出excel java_dwz怎么导出excel

热门文章

  1. CoCreateInstance(转)
  2. ARM Linux 3.x的设备树(Device Tree)【转】
  3. 设计模式之美:Memento(备忘录)
  4. 使用Aspose.Pdf for .NET实现PDF文档到Excel、EPS、SVG等的转换
  5. Word遇到问题需要关闭
  6. flutter 刷脸_GitHub - nnnggel/baidu_face_plugin: 百度人脸识别和活体检测 Flutter 插件(目前版本仅支持 Android)...
  7. 不用ajax 后台的异步实现,js 怎么避免重复的异步操作(不问了,目前没办法后台做到,只能改造前端了)...
  8. list删除某个元素_Python基础入门之列表(list)
  9. 认识下PHP如何使用 phpmailer 发送电子邮件
  10. 四川大学计算机学院男女生比例,川大、电子科大新生男女比例大PK 比例更佳的是……...