仅仅使用信号量并不能解决此问题。必须引入计数器对读者进行计数,专门定义一个互斥信号量用于对计数器的互斥访问。另一个信号量表示是否允许写的信号量。
Int count=0; Semaphore write=1,mutex=1; Void reader() { P(mutex); Count++; If(count==1) P(write);//判断是否有写者在对文件进行写操作,有的话读者阻塞。只有第一个读者才进行此判断。 V(mutex); { //读文件。 } P(mutex); Count--; If(count==0) V(write);//当所有读者都退出时释放对文件的占用。只要有读者在占用文件,就不释放对文件的占用。 V(mutex); } Void Writer() { P(write); { //写文件。 } V(write); } |
当存在读者时,写者将会被阻塞。且只要有一个读者在读文件时,随后而来的其他读者都允许访问文件,从而导致写者长时间等待,这可能出现饥饿现象。
在windows中P操作和V操作分别对应WaitForSingleObject和ReleaseSemaphore。
为获得被保护资源的访问权,进程或线程要调用一个等待函数并传入信号量的句柄,在内部等待函数会检查信号量的当前资源计数。如果它大于0,那么该函数会把资源计数器减1,调用线程继续执行。如为0,系统会让调用线程进入等待状态。所有这些操作都是原子的。
ReleaseSemaphore递增信号量的资源计数,此时会从信号量等待队列中释放一个线程,将其加入就绪队列使其变为可调度状态。
进程使用独占型资源必须按照:申请资源、使用资源、归还资源的次序。若资源不可用则申请进程进入等待态。许多情况下进程需要独占方式访问多个资源,当操作系统允许多个进程并发时,可能出现进程永远被阻塞的现象。此现象被称为死锁。产生死锁的因素不仅与系统拥有的资源数量有关,而且与资源分配策略、进程对资源的使用要求以及并发进程的推进顺序有关。
如果一个进程集合中的每个进程都在等待只能由此集合中的其他进程才能引发的时间,而无限期陷入僵持的局面称为死锁。
死锁的解决方法主要有:死锁防止、死锁避免、死锁检测和恢复。这些方法也适用于线程。