信号量Semaphore:用来表示一种资源
信号量应用:
1.实现进程之间互斥
2.实现前趋关系
当信号量的资源数小于等于0的时候标示该资源已经分配完毕,申请该资源的线程应该处于阻塞状态,放弃CPU时间,当一个线程进行释放资源的时候该信号量的资源数进行增加1,此时可以唤醒一个线程进行工作
下面使用互斥信号量解决读写者问题(利用线程进行模拟进程)
互斥信号量:如果一个信号量的资源数初始值为1该信号量成为互斥信号量实现进程之间的互斥(用线程进行模拟)模拟线程进行访问临界资源
经典的线程互斥问题(读写者问题)使用P,V原语描述
通俗的说明:
P:申请资源减少信号量类的资源数
V:释放资源增加信号量类的资源数
信号量类定义及其P,V操作定义
#ifndef _SEMAPHORE_H_ #define _SEMAPHORE_H_ #include <Windows.h> #include <stdio.h> class Semaphore { public: Semaphore(unsigned int countOfSem); ~Semaphore(); void P();//申请资源 void V();//释放资源 private: HANDLE sem;//信号量句柄 }; #endif
信号量类的实现部分:
#include "Semaphore.h" #include <Windows.h> #define MAX_COUNT_SEM 50//定义信号量类的初始资源数最大为MAX_COUNT_SEM
//对信号量进行初始化,规定信号量对象的初始个数count,当调用系统中的wait系列函数时减少(waitForSingleObject)调用ReleaseSemaphore增加 //当count>0时,一个信号量处于受信状态signaled ,wait系列函数返回 //当count<=0时,一个信号量处于未受信状态unsignaled ,进行申请资源的线程阻塞,wait系列函数不会返回 Semaphore::Semaphore( unsigned int countOfSem ) { if(countOfSem >= MAX_COUNT_SEM) { countOfSem = MAX_COUNT_SEM; } sem = CreateSemaphore(NULL,countOfSem,MAX_COUNT_SEM,NULL);//创建信号量 } Semaphore::~Semaphore() { CloseHandle(sem); } //申请资源 void Semaphore::P() { DWORD dwWaitResult; dwWaitResult = WaitForSingleObject(sem,INFINITE);//只有当信号量对象处于受信状态时该函数才进行返回 //否则进行等待 //参数使用的是INFINITE进行无限制的等待下去直到信号量对象的count值大于0处于受信状态 if(dwWaitResult == WAIT_OBJECT_0) { //可以进行工作 } else { //出现错误不会出现超时现象因为设置的等待时间为无限制的等待 } } //进行释放资源 void Semaphore::V() { ReleaseSemaphore(sem,1,NULL);//释放资源(使得信号量的个数count++) }
读写者问题算法分析:
一个数据库资源允许和禁止操作
(1)多个读者进行读操作 允许
(2)一个写者多个读者 禁止
(3)多个读者 禁止
(4)多个写者 禁止
只要存在写数据库的线程其他线程如果要进行读或者写都要进行阻塞
1.为了实现Reader和Writer必须要进行设置一个互斥信号量表示数据库资源
2.设置一个ReadCount标示当前读取数据库的读者的个数如果当前读者的个数为0,Reader进程进行读取数据库的时候才要进行申请资源,如果申请成功则可以进行读取操作其他的读取线程不同继续的申请数据库资源可以进行读取操作,同时将ReadCount的值进行相应的增加
3.当一个Reader线程完成数据库的操作的时候,执行ReadCount减少1,当ReadCount的值为0的时候才进行释放资源写者线程可以进行申请数据库的权限
全局变量定义
//都为互斥信号量因为信号量资源数的初始值都为1,表示该资源是一种临界资源
int ReadCount = 0;//读者的个数 Semaphore mutex_Cout(1);//用户保护输出语句 Semaphore mutex_DataBase(1);//代表数据库可以是否可以进行读写的信号量 Semaphore mutex_Gl(1);//对全局变量ReadCount进行互斥保护
读者线程函数实现:
unsigned int WINAPI Reader(void *param) { while (true) { mutex_Gl.P();//互斥保护共享全局变量ReadCount if(ReadCount == 0)//如果是第一个读者申请读取数据库则进行申请数据库资源 { mutex_DataBase.P(); } ReadCount++; mutex_Gl.V(); //开始进行读取数据库动作 mutex_Cout.P(); cout << "有读者在进行读取数据库操作" << endl; mutex_Cout.V(); //将读者技术ReadCount减一如果是最后一个读者读取完数据库则进行释放占据的数据库资源否则不进行释放资源 mutex_Gl.P(); ReadCount--; if(ReadCount == 0)//如果是最后一个读者完成读取的任务则释放数据库资源 { mutex_DataBase.V(); } mutex_Gl.V(); Sleep(rand()%1000); } return 0; }
写者线程实现:
unsigned int WINAPI Wirter(void *param) { while (true) { mutex_DataBase.P();//尝试申请数据库资源 mutex_Cout.P(); cout << "Writing Database" << endl;//进行读取数据库操作 mutex_Cout.V(); mutex_DataBase.V();//释放数据库资源 Sleep(rand()%1000); } return 0; }
主函数实现:
int main() { UINT uID; HANDLE hReader[READERCOUNT];//假设有十个读者 HANDLE hWriter;//假设有一个写者//开启代表读者的十个线程 for(int i = 0;i<READERCOUNT;i++) { hReader[i] = (HANDLE)::_beginthreadex(NULL,0,Reader,NULL,0,&uID); } hWriter = (HANDLE)::_beginthreadex(NULL,0,Wirter,NULL,0,&uID);//开启代表写者的一个线程 ::WaitForMultipleObjects(READERCOUNT,hReader,TRUE,INFINITE);//等待所有的进程结束 ::WaitForSingleObject(hWriter,INFINITE); for(int i = 0;i<READERCOUNT;i++) { ::CloseHandle(hReader[i]); } ::CloseHandle(hWriter); return 0; }
运行结果:
信号量初始资源数的不同可以用来解决不同的问题,接下来进行讨论解决师傅打铁问题,生产者消费者问题