进程同步是指在多道程序的环境下,存在着不同的制约关系,为了协调这种相互制约的关系,实现资源共享和进程协作,从而避免进程之间的冲突,进而引入进程同步。
临界资源
对于某些资源来说,在同一时间只能被一个进程所占用,这些资源就被成为临界资源。典型的临界资源比如物理上的打印机等等。
对于临界资源的访问,必须是互斥的,也就是当临界资源被占用时,另一个申请临界资源的进程会被阻塞,直到其所申请的临界资源被释放。进程内访问临界资源的代码被称为临界区。
进程同步
指为了完成某任务而建立的两个或多个线程,这些线程需要在某些位置上协调他们的工作而等待、传递信息所产生的制约关系。
比如B进程需要A进程执行完毕才能执行,所以为了等待A进程,B进程就先会阻塞,等A进程执行完毕之后才会去执行。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k09jxWW4-1615636902447)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6b0ee893-ac6f-4763-b673-fb603ddb26ea/0603E3BA-6A21-4BC8-93D2-CD58C6625191.jpeg)]
进程互斥
某个进程进入临界区后,另外一个想要进入临界区的进程必须等待,只有当临界区中的进程退出临界区后,等待的进程才可以进入临界区。
实现临界区互斥的方法
信号量
也就是PV操作。设置一个表示资源个数的信号量S,通过对S的P、V操作来实现进程的互斥。
P也就是占有,也就是对S进行减操作,表示有一个进程将占有或者等待资源,如果S≥0则继续,否则将进程阻塞,并放入阻塞队列。
V也就是释放,也就是对S进行加操作,表示有一个占用资源的进程释放了资源,如果S≤0则会从阻塞队列中唤醒一个进程来执行。
信号量和互斥量的区别
- 信号量:多线程同步使用的;一个线程完成某个动作后通过信号告诉别的线程,别的线程才可以执行某些动作;
- 互斥量:多线程互斥使用的;一个线程占用某个资源,那么别的线程就无法访问,直到该线程离开,其他线程才可以访问该资源;
生产者消费者问题
需要三个信号量:empty(空资源)、full(满资源)、mut(互斥)
生产者生产资源时,就P(empty) V(full),并且在进入临界区时P(mut)出来时V(mut)。
消费者消费资源时,就P(full) V(empty),并且在进入临界区时P(mut)出来时V(mut)。
哲学家就餐
哲学家只需要一个信号量,但是他的核心在于他对筷子的控制。当有5位哲学家时:
/*当哲学家饥饿时,总是先拿左边的筷子,再拿右边的筷子*/
// P操作
wait(chopstick[i]);
wait(chopstick[(i+1)%5]);
// 吃饭
/*当哲学家进餐完成后,总是先放下左边的筷子,再放下右边的筷子*/
// V操作
signal(chopstick[i]);
signal(chopstick[(i+1)%5]);
读者写着问题
读者和写者之间互斥信号量wrMutex,代表读者个数的信号量信号量readCount,以及保护readCount的互斥信号量rMutex。
互斥信号量wrt,初值是1,代表一个共享文件,解决“读-写”互斥,“写-写”互斥。
一个记数器,即整型变量readcount,记录读者数,初值是0。 来一个读者, readcount加1 当readcount =1表示是第一个读者, 则需要执行p操作抢占文件;否则表示已有读者在安全的读数据。 走一个读者,readcount减1 当readcount =0表示是最后一个读者,则需要v操作释放资源;否则表示还有读者在读数据。
所以对于读者来说,开始读之前,得让readCount加一,并且这段操作需要使用到rMutex互斥信号量;读完之后也得让readCount—,这段操作也得需要rMutex互斥信号量。中间的操作需要用到wrMutex,先P再V。
写者,直接先P再V
int readcount=0;
semaphore mutex=1, wrt=1 ;
读者进程:
wait (mutex);
readcount++;
if (readcount == 1)
wait(wrt);
signal (mutex);
…
reading is performed
…
wait (mutex);
readcount--;
if (readcount == 0)
signal (wrt);
signal (mutex);
写者进程:
wait(wrt);
…
writing is performed
…
signal(wrt);
银行家算法
当一个进程申请使用资源的时候,银行家算法通过先 试探 分配给该进程资源,然后通过安全性算法判断分配后的系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AXXPGOn0-1615636902448)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/52877484-62dd-49b3-b7a1-4f7b6dff6634/Untitled.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3fYkMl1-1615636902452)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1a848e2c-3b36-47e3-b34c-439ff3d07551/Untitled.png)]
线程同步的方式主要有: 临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)。
他们的主要区别和特点如下:
**1)临界区:**通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,
如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。
**2)互斥量:**采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。
互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
**3)信号量:**它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
4)事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作。