一、具体实现:
CSemaphore::CSemaphore(LONG lInitialCount,LONG lMaxCount,LPCTSTR pstrName,
LPSECURTY_ATTRIBUTES lpsaAttribute):CSyncObject(pstrName)
CSemaphore::~CSemaphore()
_AFXMT_INLINE BOOL CSemaphore::Unlock()
BOOL CSemaphore::Unlock(LONG lCount,LPCTSTR lpPrevCount)
二、关于带参数的 Lock()函数的使用(例子)
用一个二维数组模拟三个串行端口
在这个例子用到三个对象:信号量,用于对串行口的资源计数
互斥量:修改串行口使用标志数组
临界段:用于向屏幕输出结果
先创建一个MFC支持的控制台工程
int Buffer[3][5];//模拟串行端口
CSemaphore semaphore(3,3,NULL,NULL);//用于访问串行口的信号量
BOOL Flag[3]={TRUE,TRUE,TURE};
CMutex mutex;//用于保护 Flag[3]的互斥量
CCriticalSection section;//用于访问显示器的临界段
从一端口读数据后向另一端口写数据的线程ReadAndWriteThread :
semaphore.Lock();//获串行口 A,此时信号量计数值减1
mutex.Lock(); //用来修改Flag
//标记串行口 A 被占用
mutex.Unlock();
semaphore.Lock();//获串行口 B ,此时信号量计数值减1
mutex.Lock();
//标记串行口 B 被占用
mutex.Unlock();
section.Lock();//获得了两个串行口 ,因为要向屏幕输出以观看,所以临界段保护
//从 A 读数据
//向 B 写数据
section.Unlock();
mutex.Lock();
//释放对串行口A,B的占用:更改标记
mutex.Unlock();
semaphore.Unlock(2);//释放两个信号量
写线程:
run=FALSE;
while(!run) { Sleep(0); run=semaphore(10); }//由于在主函数要创建多个线程,所以在这采用试探法。
先在一段时间等待资源,若不能,则放弃,过段时间再试探。
如此一直循环,直到获得资源,这样可避免死锁问题。
Sleep(0)意思是放弃分配给该线程的剩余时间片。
mutex.Lock();
//标记 串行口 被占用
mutex.Unlock();
section.Lock();//向串行口写数据 ,因为要向屏幕输出以观看,所以临界段保护
//向 串行口 写数据
section.Unlock();
mutex.Lock();
//写完数据后就释放对串行口 的占用:更改标记
mutex.Unlock();
semaphore.Unlock();//释放两个信号量
主函数:
if(初始化MFC失败) {//打印失败信息}
else
{
for(int i=0;i<6;i++)
::AfxBeginThread(WriteThread,i+1);//在这创建了6个写线程
::AfxBeginThread(ReadAndWriteThread,NULL);
}
此程序的运行流程是:
1、首先创建了6个写线程,每个线程都使用信号量,由于信号量的最大计数值是3,所以写了3组Buffer数据;
2、线程写完数据释放了信号量之后,马上被 读写线程获得信号量的计数,于是进行了读写操作。
3、于读写操作只占用了2个信号量,还剩一个信号量继续被写线程所获得。于是又向第三排Buffer写数据;
4、读写操作同时释放了连个信号量后,又马上被写线程获得,于是又向第一、二排Buffer写数据。
三、小结
由此可以看出:
1、信号量主要是计数的作用,可以很好的控制同一时刻能运行的线程数目。
2、互斥量只要控制某一变量或资源在同一时刻不能被多个线程所拥有和操作。
3、临界段主要是在某个区域不能同时运行某个操作。