操作系统中同步、异步性概念

首先我们从操作系统的发展中学习什么是异步性。在操作系统发展的初期阶段,CPU处理的是作业,而且是单道批处理。什么意思呢?就是一个作业从提交到结束,程序员都不能干预,此时整台计算机就为这一个作业服务(可想有多少资源被"浪费"),这样有一点好处就是整个程序是"封闭的"。这样的操作表明人和机器是没有交互的。那我们怎么实现人机交互呢?这个答案是中断。中断的引入,使得工作人员能在程序运行出问题的时候也能做出相应的处理。那么在当前程序中断后,计算机总不能让CPU不做事吧,所以人们引入了新的概念——进程。当A进程不能继续执行的时候(可能是因为资源不足、竞争,或是等待I/O处理),A进程会阻塞,而B进程有足够的资源,这时操作系统便把CPU分配给B进程。当然,这里还涉及到了中断处理程序。当A进程让出CPU之前,中断处理程序要做的是保护现场,即A进程的相关参数。当A进程等待的事件完成了,便可以返回中断点重新开始工作。

简单介绍发展史有助于我们更深刻的理解异步性的概念(当时我就是这样一步一步把异步性、同步概念串起来的)。进程引入后,让CPU的吞吐量得到了提升(若是单道批处理,作业等待I/O,那么这个时候CPU也要等)。但带来的问题是程序的运行失去了封闭性异步性是指进程以不可预知的速度向前推进。在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系(一般是资源问题)。内存中的每个进程何时执行,何时暂停,以怎样的速度向前推进,程序总共需要多少时间才能完成等,都是不可预知的。例如,当正在执行的进程提出某种资源请求时,如打印机请求,而此时打印机正在为其他的进程打印。由于打印机是临界资源,因此正在执行的进程必须等待,并且要放弃处理机。直到打印机空闲,并再次把处理机分配给该进程时,该进程才能继续执行。由于资源等因素的限制,进程的执行通常都不是 一气呵成,而是以 停停走走 的方式运行。

试想以下两个简单的小程序是两个进程,其中i是公共资源。

#include<stdio.h>//程序A
int i = 1;
int main()
{i = i + 10;//中间包含若干与i无关的操作printf("Ai = %d", i);return 0;
}#include<stdio.h>//程序B
int i = 1;
int main()
{i++;//中间包含若干与i无关的操作printf("Bi = %d", i);return 0;
}

由于A和B是并发执行的,并且推进速度是不可预知的,所以最终的结果有多种情况。以下只分析两种①:Ai = 11 Bi = 12 即:A先运行i = i + 10;并打印出来,B再运行i++。②:Ai = 12 Bi = 12 即:A先运行 i = i + 10此时并没有打印,而B运行了i++后,A和B分别将i的值打印出来。其他情况也可以这样分析。因为异步性的关系,我们得到的答案可能是错误的,或是我们不想要的。为了解决这个问题,就必须引入同步机制,使得程序能按照规则运行下去,从而得到我们想要的答案。

创建父子进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{pid_t pid =fork();if( pid == 0 )   //子进程返回值为0{while(1){printf("This is child\n");sleep(1);}}else{while(1){printf("This is parent\n");sleep(1);}}return 0;
}

可以看到,父子进程之间打印的信息并没有固定的先后顺序。当父子进程同时去访问资源时,也不能确定获取资源的先后顺序。这就表明进程的异步性可能出现我们不想要的结果,或者说是错误的结果。解决上述问题的方法就是"同步"。

同步亦称直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系就是源于它们之间的相互合作。例如:上面两个程序可以看成5+3*5中的加法程序和乘法程序。若先执行乘法程序再执行加法程序,则5+3*5=20。这个答案一定是对的吗?其实不然。如果我们想要的答案是40,就要先执行加法程序再执行乘法程序,(5+3)*5=40。我们通过加 () 改变了运算的先后顺序,使先乘除后加减变成了先加减后乘除,这就是一种同步机制。

当我们理解了异步、同步的概念后,可以简单了解一下互斥的概念。互斥亦称间接制约关系。当一个进程进入临界区使用临界资源时,另一个进程必须等待,当占用临界资源的进程退出临界区后,另一进程才允许去访问此临界资源。例如,在仅有一台打印机的系统中,有两个进程A和进程B,如果进程A需要打印时,系统已将打印机分配给进程B,则进程A必须阻塞。一旦进程B将打印机释放,系统便将进程A唤醒,并将其由阻塞状态变为就绪状态。为禁止两个进程同时进入临界区,同步机制应遵循以下准则:

空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。

忙则等待:当已有进程进入临界区时,其他试图进入临界区的进程必须等待。

有限等待:对请求访问的进程,应保证能在有限时间内进入临界区。

让权等待:当进程不能进入临界区时,应立即释放处理器,防止进程忙等待。

线程同步例子(使用互斥锁):

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>/*全局变量*/
int sum = 0;
/*互斥量 */
pthread_mutex_t mutex;
/*声明线程运行服务程序*/
void* pthread_function1 (void*);
void* pthread_function2 (void*);int main (void)
{/*线程的标识符*/pthread_t pt_1 = 0;pthread_t pt_2 = 0;int ret = 0;/*互斥初始化*/pthread_mutex_init (&mutex, NULL);/*分别创建线程1、2*/ret = pthread_create( &pt_1,                  //线程标识符指针NULL,                  //默认属性pthread_function1,     //运行函数NULL);                 //无参数if (ret != 0){perror ("pthread_1_create");}ret = pthread_create( &pt_2,                  //线程标识符指针NULL,                   //默认属性pthread_function2,      //运行函数NULL);                  //无参数if (ret != 0){perror ("pthread_2_create");}/*等待线程1、2的结束*/pthread_join (pt_1, NULL);pthread_join (pt_2, NULL);printf ("main programme exit!\n");return 0;
}/*线程1的服务程序*/
void* pthread_function1 (void*a)
{int i = 0;printf ("This is pthread_1!\n");for( i=0; i<3; i++ ){pthread_mutex_lock(&mutex); /*获取互斥锁*//*临界资源*/sum++;printf ("Thread_1 add one to num:%d\n",sum);pthread_mutex_unlock(&mutex); /*释放互斥锁*//*注意,这里以防线程的抢占,以造成一个线程在另一个线程sleep时多次访问互斥资源,所以sleep要在得到互斥锁后调用*/sleep (1);}pthread_exit ( NULL );
}/*线程2的服务程序*/
void* pthread_function2 (void*a)
{int i = 0;printf ("This is pthread_2!\n");for( i=0; i<5; i++ ){pthread_mutex_lock(&mutex); /*获取互斥锁*//*临界资源*/sum++;printf ("Thread_2 add one to num:%d\n",sum);pthread_mutex_unlock(&mutex); /*释放互斥锁*//*注意,这里以防线程的抢占,以造成一个线程在另一个线程sleep时多次访问互斥资源,所以sleep要在得到互斥锁后调用*/sleep (1);}pthread_exit ( NULL );
}Linux下编译时需要加 -lpthread注意第一个字母是大写,windows C语言中单位是毫秒(ms)。
Sleep (500);
就是到这里停半秒,然后继续向下执行。
包含在#include <windows.h>头文件在Linux C语言中 sleep的单位是秒(s)
sleep(5);//停5秒
包含在 #include <unistd.h>头文件

由于程序先创建的是 Thread_1,所以 Thread_1 先加锁,即拥有使用公共资源的权限。Thread_1 在加锁后休眠2秒,此时 Thread_2 被阻塞。若不加锁,Thread_2 能直接对公共资源进行操作。当 Thread_1 的工作完成,它释放互斥锁资源,之后运行 Thread_2。同理,当 Thread_2 运行时,Thread_1 被阻塞,直至 Thread_2 完成工作并释放互斥锁资源。

经典进程同步问题:生产者-消费者问题

(1) 描述:一组生产者进程和一组消费者进程共享一个初始为空、大小为 n 的缓冲区,只有缓冲区没满时,生产者才能把消息放入到缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或者一个消费者从中取出消息。

(2) 分析:

① 关系分析。生产者和消费者对缓冲区互斥访问是互斥关系,同时生产者和消费者又是一个相互协作的关系,只有生产者生产之后,消费者才能消费,他们也是同步关系。

② 整理思路。这里比较简单,只有生产者和消费者两个进程,正好是这两个进程存在着互斥关系和同步关系。那么需要解决的是互斥和同步 PV 操作的位置。

③ 信号量设置。信号量 mutex 作为互斥信号量,它用于控制互斥访问缓冲池,互斥信号量初值为1;信号量 full 用于记录当前缓冲池中

"满"缓冲区数,初值为0。信号量 empty 用于记录当前缓冲池中"空"缓冲区数,初值为 n。生产者-消费者进程的伪代码如下:

semaphore mutex=1; //临界区互斥信号量
semaphore empty=n; //空闲缓冲区
semaphore full=0;  //缓冲区初始化为空
producer () {      //生产者进程while(1){produce an item in nextp;  //生产数据P(empty);  //获取空缓冲区单元P(mutex);  //进入临界区.add nextp to buffer;  //将数据放入缓冲区V(mutex);  //离开临界区,释放互斥信号量V(full);  //满缓冲区数加1}
}consumer () {     //消费者进程while(1){P(full);  //获取满缓冲区单元P(mutex); // 进入临界区remove an item from buffer;  //从缓冲区中取出数据V (mutex);  //离开临界区,释放互斥信号量V (empty) ;  //空缓冲区数加1consume the item;  //消费数据}
}

该类问题要注意对缓冲区大小为 n 的处理,当缓冲区中有空时便可对 empty 变量执行 P 操作,一旦取走一个产品便要执行 V 操作以释放空闲区。对 empty 和 full 变量的 P 操作必须放在对 mutex 的P操作之前。如果生产者进程先执行 P(mutex),然后执行 P(empty),消费者执行 P(mutex),然后执行P(fall),这样可不可以?答案是否定的。设想生产者进程已经将缓冲区放满,消费者进程并没有取产品,即empty = 0,当下次仍然是生产者进程运行时,它先执行 P(mutex) 封锁信号量,再执行 P(empty) 时将被阻塞,希望消费者取出产品后将其唤醒。轮到消费者进程运行时,它先执行 P(mutex),然而由于生产者进程已经封锁 mutex 信号量,消费者进程也会被阻塞,这样一来生产者、消费者进程都将阻塞,都指望对方唤醒自己,陷入了无休止的等待。同理,如果消费者进程已经将缓冲区取空,即 full = 0,下次如果还是消费者先运行,也会出现类似的死锁。不过生产者释放信号量时,mutex、full 先释放哪一个无所谓,消费者先释放mutex 还是 empty 都可以。

操作系统中的同步和异步相关推荐

  1. 计算机操作系统的同步和异步,操作系统中的同步和异步有什么区别?分别应用在什么场合?...

    同步,就是说你的程序在执行某一个操作时一直等待直到操作完成.    最常见的例子就是 SendMessage.该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回.当对方处理完毕以后,该 ...

  2. iOS网络开发中的同步、异步和请求队列

    在iOS网络编程中,我们经常会遇到线程的同步和异步问题,同时为了对异步请求更加精准丰富的控制,我们还常常在iOS中使用请求队列,下面就来谈谈iOS开发中同步.异步以及请求队列的使用方法. 1. 同步意 ...

  3. 微软BI 之SSIS 系列 - 理解Data Flow Task 中的同步与异步, 阻塞,半阻塞和全阻塞以及Buffer 缓存概念...

    开篇介绍 在 SSIS Dataflow 数据流中的组件可以分为 Synchronous 同步和 Asynchronous 异步这两种类型. 同步与异步 Synchronous and Asynchr ...

  4. java 同步异步_Java中的同步于异步

    java异步同步应用 所谓异步输入输出机制,是指在进行输入输出处理时,不必等到输入输出处理完毕才返回.所以异步的同义语是非阻塞(None Blocking). 网上有很多网友用很通俗的比喻 把同步和异 ...

  5. java中的同步与异步

    经常看到介绍 ArrayList 和HashMap是异步,Vector和HashTable是同步,这里同步是线程安全的,异步不是线程安全的,举例说明: 当创建一个Vector对象时候, Vector ...

  6. 理解js中的同步和异步

    首先要先了解下js单线程 一.为什么js是单线程? 其实,JavaScript的单线程,与它的用途是有很大关系,我们都知道,JavaScript作为浏览器的脚本语言,主要用来实现与用户的交互,利用Ja ...

  7. 如何理解js中的同步和异步

    首先需要理解:JS是单线程运行的 同步和异步,无论如何,做事情的时候都是只有一条流水线(单线程),同步和异步的差别就在于这条流水线上各个流程的执行顺序不同. 同步就是程序按照正常的执行顺序,依次执行 ...

  8. Java中的同步和异步

    一.前言 在多线程的环境中,经常会碰到数据共享问题,即当多个线程需要访问同一个资源时,它们需要以某种顺序来确保该资源在某一时刻只能被一个线程使用,否则,程序的运行结果将会是不可预料的,在这种情况下就必 ...

  9. python中的 同步与异步 互斥锁 和 死锁

    同步与异步: 同步:指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去. 异步:指进程不需要一直等下去,而是继续执行下面的操 ...

最新文章

  1. Controller节点无法启动neutron-server
  2. 学生管理系统——基于双向循环链表
  3. mysql 二元分词_MySQL 中文分词原理
  4. Apollo的基本概念和集成实战
  5. 002 exercises
  6. linux kill -9 杀不掉的进程
  7. LeetCode 155. Min Stack
  8. tensorflow rnn 最简单实现代码
  9. 【C++】 29_类中的函数重载
  10. visio软件接口流程图_用Visio制作最专业的技术流程图教程
  11. 星环科技TDH基于Overlay网络架构为数据安全保驾护航
  12. Boost PFC参数计算——交流电的基本概念与推导
  13. 总结51单片机之上拉电阻
  14. qq快捷登陆 php代码,qq互联--qq快捷登陆
  15. 外汇交易中的结汇,售汇,收汇,付汇是什么意思
  16. NVIDIA GeForce Go 6100 for Wndows 7 (笔记本)
  17. 五大领域总目标指南_五大领域总目标和各年龄段目标 -
  18. 在2012年08月08号这一天,2345浏览器的V2.0版本跑出来了!
  19. 车载FMCW雷达的距离-多普勒检测基本原理
  20. linux cscope界面,Linux cscope代码阅读工具配置

热门文章

  1. BOOST内存管理(一) --- boost::object_pool
  2. IP地址的三种表示格式 及 在Socket编程中的应用
  3. Android平台Chromium net中的代理配置信息获取
  4. nginx如何开启debug日志及相关配置
  5. 搞定系统设计 01:从 0 到百万用户的系统
  6. B站开源自研动漫超分辨率模型,助力动漫UGC视频高清化
  7. 与WebXR共同创建者Diego Marcos一起探讨沉浸式Web的未来(上)
  8. 音视频技术开发周刊 | 180
  9. 音视频技术开发周刊(第131期)
  10. 视频加速方案的最优解 - Xilinx硬件加速技术专场(深圳站)