进程同步与通信
进程同步与通信
进程的互斥与同步
基本概念
- 进程互斥:进程互斥是指某一资源同一时间只允许一个进程对其进程访问,这种访问具有唯一性和排他性。
- 进程同步:进程同步是指某些进程之间在逻辑上的相互制约关系。也就是说,若干个进程为完成一个共同的任务而相互合作,由于合作的每个进程都是以各自独立的、不可预知的速度向前推进,这就需要相互合作的进程在某些协调点来协调他们的工作。
临界资源与临界区
- 临界资源:把一段时间内只允许一个进程使用的资源称为临界资源
- 临界区:把进程中访问临界资源的代码段称为临界区
信号量机制/PV原语
信号量机制是一种在并发编程中用于同步和互斥的机制。它由荷兰计算机科学家 Edsger Dijkstra 在1965年引入,是一种在多个进程或线程之间共享资源时控制访问的方法。信号量机制通常使用两个基本操作:P(也称为 Wait 或者 Down 操作)和 V(也称为 Signal 或者 Up 操作)。
PV 原语是用来操作信号量的两个原子操作,其中 P 操作(原语)用于获取(减少)信号量的值,V 操作(原语)用于释放(增加)信号量的值。
- P 操作(Wait 操作):
- 如果信号量的值大于 0,则将信号量的值减 1,程序继续执行。
- 如果信号量的值等于 0,则进程(或线程)被阻塞,并等待信号量的值变为大于 0。
- V 操作(Signal 操作):
- 如果有进程(或线程)因为等待信号量而被阻塞,则唤醒其中一个被阻塞的进程(或线程)。
- 如果没有进程(或线程)因为等待信号量而被阻塞,则将信号量的值加 1。
信号量机制的主要目的是实现对共享资源的互斥访问和同步操作。通过使用 P 和 V 操作对信号量进行加锁和释放锁的操作,可以确保同一时间只有一个进程(或线程)能够访问共享资源,从而避免了竞态条件(Race Condition)的发生。
信号量机制还可以用于解决经典的并发编程问题,如生产者-消费者问题、读者-写者问题和哲学家就餐问题等。通过合理地使用信号量,可以实现线程间的协调和资源的合理分配,确保系统的正确性和稳定性。
需要注意的是,信号量机制仅提供了一种低级别的同步原语,使用不当可能导致死锁(Deadlock)或活锁(Liveloack)等问题。因此,在实际应用中,通常会使用更高级别的同步机制(如互斥锁、条件变量等)来构建更可靠和安全的并发程序。
管程
管程(Monitor)是一种高级的同步机制,用于解决并发编程中的同步和互斥问题。它由荷兰计算机科学家 C.A.R. Hoare 在1974年提出,是一种封装了共享变量和操作的抽象数据类型。
管程提供了一种结构化的方式来管理共享资源,它包含了一组数据和一组操作(也称为方法或过程)。在管程内部,只有一个进程(或线程)可以执行操作,其他进程(或线程)必须等待。这样可以确保对共享资源的访问是互斥的,避免了竞态条件的发生。
管程的主要特点包括:
- 互斥访问:管程内部的操作是互斥的,同一时间只有一个线程可以进入管程执行操作。这样可以避免多个线程同时访问共享资源而导致的数据不一致性问题。
- 条件变量:管程可以定义一个或多个条件变量,用于线程间的等待和唤醒操作。线程可以通过条件变量的等待操作(如 wait)来进入等待状态,直到其他线程发出相应的信号(如 signal)来唤醒它们。
- 互斥锁:为了确保对共享资源的互斥访问,管程通常会使用互斥锁(Mutex)来保护共享变量。互斥锁可以确保同一时间只有一个线程可以获得对共享变量的访问权限。
- 数据抽象:管程通过封装共享资源和操作,提供了一种高级的抽象,使得并发编程变得更加易于理解和编写。它隐藏了底层的同步细节,使得程序员可以更专注于问题的逻辑而不必担心并发问题。
管程的使用可以简化并发程序的设计和实现,提高代码的可读性和可维护性。它在许多编程语言和操作系统中得到了广泛的应用和支持,例如Java中的synchronized关键字和C++中的std::mutex类。
需要注意的是,管程并不是一种通用的解决方案,它适用于一些特定的同步问题。在某些情况下,其他同步机制(如信号量、读写锁等)可能更适合解决特定的并发问题。因此,在选择同步机制时,需要根据具体的情况和需求进行权衡和选择。
进程通信
概念
同步与通信是并发进程交互的两个基本要求。进程同步主要解决临界区问题,而进程通信主要指进程之间的信息交换。
进程通信的方式
- 低级通信
- 信号量机制/PV原语
- 高级通信
- 共享内存通信方式
- 消息传递通信方式
- 消息缓冲通信方式
- 信箱通信方式
- 共享文件通信方式
- 管道通信方式