昨天接到操作系统课程设计题目,是用信号量同步进程(线程)的,由于要用到Windows API ,好几个同学问我API是干什么的,由于大家都没接触过,不知怎么用,所以我就以课设题目为例,给大家介绍一下API的一些基本知识。但请您注意,本文旨在介绍API使用,而不是给您提供现成的代码让您直接复制到课程设计报告中去,您应该只把这做为一份参考,然后写出自己的代码。好了,我们从最基本的讲起,首先看题目:
Linux或Windows或Unix环境下,采用系统调用中的信号量、P、V操作,编程解决以问题。读者与写者问题。一个数据集为多个并发进程所共享,其中一些进程只要求读该数据集的内容,这些进程称为“读者”,而另一些进程则要求修改该数据集的内容,这些进程称为“写者”。具体要求是:允许多个读者同时读该数据集的内容;若有一个写者在写,则其他读者不能读;若一个写者在写或有其他读者在读,则其他写者均被拒绝;当一个写者正在写,而有多个读者与写者在等待时,写者应优先唤醒。请用P、V操作写出进程的同步算法。要求打印:初始状态,中间变化的状态信息,以及最终状态信息。
我以Windows做为示例,考虑到大家并非都懂C++,故代码用C写。分析:
1.理想状态:我们程序中完全和作业中的方式一样,比如定义信号量用 semaphore mutex; P操作就直接P(mutex),V操作就直接V(mutex),尽量避免直接使用API。
好的,我们首先实现这两种功能。由于笔者平时开发习惯,都会在特定的工程中的函数、数据类型加上工程标志,这个项目是Operating system Course Design,我就取OSCD,做为函数和数据类型前缀。所以上面的semaphore mutex;定义信号量的方式就变为OSCD_semaphore mutex(只是换了个衣服而已)。下面介绍第一个API函数(为了先避免字符编码的问题,看我这个不要对函数名后的A太过在意,以后接触Windows编程后,你自然就懂了):
HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCSTR lpName );首先看一下这个中的数据类型;其实 LONG 就是typedef long LONG; LPCSTR 就是char * (字符串的指针),LPSECURITY_ATTRIBUTES 和HANDLE目前不是一两句话能将清楚的,LPSECURITY_ATTRIBUTES 可以先直接忽略掉,你可以把HANDLE 理解为操作信号的“手柄”,也可以先理解为指向信号量的类型,以后我们对信号量的操作就是通过它来完成的,在直接一点,我们就可以把它就当作是信号量,其中lInitialCount就好比我们作业里mutex.value,中的value,我们需要在调用这个函数时给它赋初值,lMaximumCount是指定信号量的最大值,本例中我们取10,最后一个参数lpName 课设用不到,直接传NULL。回过头来看看这个函数实现的功能:创建一个信号量并返回,在创建信号量时我们必须对信号量赋初值。也就是说我们真正用到的参数只有一个lInitialCount,下面我们对其封装;
typedef HANDLE OSCD_semaphore //这样以后就可以直接这样 OSCD_semaphore mutex;定义信号量了;
#define MAX_SEM_COUNT 10 //信号量最大值我们取10
OSCD_semaphore OSCD_CreateSemaphore(int nInitialCount) //参数是初值,返回的是信号量。
{
OSCD_semaphore hSemaphore;
if (nInitialCount>MAX_SEM_COUNT){ //
printf("semaphore count too big ,please specify a value range 0 to 10\n");
exit(0);
}
hSemaphore=::CreateSemaphoreA(NULL,nInitialCount,MAX_SEM_COUNT,NULL );//真正创建信号量
if (hSemaphore == NULL) {
printf("CreateSemaphore error: %d\n", GetLastError());
exit(0);
}
return hSemaphore;
}
拿本例中的 mutex 为例;我们只需要这么初始化它:OSCD_semaphore mutex;//定义mutex, mutex= OSCD_CreateSemaphore(1);//初始化;下面是删除信号量的函数:
void OSCD_DelSemaphore( OSCD_semaphore semaphore)
{
::CloseHandle(semaphore);//你可以认为这个API可以删除掉用上面函数创建的信号量,要删除那个信号量,参数就是哪个信号量,比如OSCD_DelSemaphore(mutex);
}
P操作:按照P操作的定义,先对信号量值减1,然后再和0比较,若大于等于0,就可以得到资源继续运行,小于0的话就等待。我先给出代码再做解释:
void P( OSCD_semaphore semaphore)
{
::WaitForSingleObject(semaphore,INFINITE);//这个API是等待信号量的函数,若等待的信号量值大于等于0时,便返回,程序继续运行,若无资源了,便会一直等待,函数不会返回,程序就不能再往下运行
}
V操作:使信号量加1;很简单:
void V(OSCD_semaphore semaphore)
{
if (!::ReleaseSemaphore(semaphore,1,NULL))//该API使信号量加1(第二个参数,如果你想使它一次加2,只需给第二个参数传2 即可)
{
printf("release semaphore error: %d\n",GetLastError());//检查上面函数是否调用成功
exit(0);
}
}//该函数使用方法,还以mutex为例说明:V(mutex);
至此我们的初步目标已经实现,接下来就是创建线程的函数;
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags, Lpdword lpThread);
去枝叶剪页,其中只有第3个和第4个参数有用,LPTHREAD_START_ROUTINE 是一个线程函数指针(其实你也可以把线程理解为特殊的函数,特殊主要体现在它能和其它线程函数并发执行)函数的原型是这样的DWORD WINAPI threadfunction(LPVOID lpParameter),意思是,这个函数有一个空指针类型的参数,该参数返回值是DWORD(unsigned long)型的,WINAPI 就是_stdcall 是函数的一种调用方式(如果现在不懂,没关系,以后会懂的,不必纠结),第四个参数是一个空类型指针,仔细看,这个参数其实就是传递给线程函数的,赶紧看线程函数的参数是不是正是LPVOID lpParameter,我们可以把写者要写的内容的指针放到这个参数里面传递给线程函数,让线程函数执行时会从这个参数所指的内容取出然后再写进缓冲区。总结一些,也就是我们要创建一个线程就必须提供一个固定的函数指针(也就是函数名),然后再把要传给线程的数据的指针给lpParameter。好了我给出调用这个函数的示例:
DWORD WINAPI Writer(LPVOID lpParameter){
写者的具体代码…
}
char * p="DataToWrite";
HANDLE hThreads=::CreateThread(NULL,0,Reader,p,SW_NORMAL,NULL);//只需注意第3、4个参数就行,其它的参数不要管。
这样线程创建就完了;但看一下这种情况 //把创建线程作为最后一个语句时main函数会在创建完线程后退出,也就意味着程序结束,但这是线程只是创建成功,但并没有执行的机会。
int main(){
………..
HANDLE hThreads=::CreateThread(NULL,0,Reader,p,SW_NORMAL,NULL);
}
要解决这个问题,就要用到另一个API,在这个实验中一共有多个线程,我们必须在创建完线程后等待它们全部运行结束后才能让main函数退出。
WaitForMultipleObjects( DWORD nCount, HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);这个函数中第一个参数指得是要等待线程的总数量,第二个参数是所有线程的“手柄”数组,因为等待函数是通过线程的“手柄”才能操作线程,等待函数也一样,只有得到它的句柄才能在其内核对象上等待;所以我们应该这样做:
HANDLE hThreads[12];//本例中有六个读者线程,六个写者线程;
然后再创建线程成功后把每个线程的句柄都保存在这个数组中WaitForMultipleObjects( DWORD nCount, HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);然后再这样调用:
WaitForMultipleObjects(12,hThreads,TRUE,INFINITE); //这个函数会在所有等待的线程都执行完成后返回,接着main函数返回,程序结束。由于这个函数也可用于等待其中任一形成结束,后返回(第三个参数传FALSE时,意思是不必等所有线程都执行完,只要有一个执行完,就返回),所以我们在这传TRUE,并且在最后一个参数(等待超时,如果在这个时间内线程还没有结束,该函数也会返回,所以我们应该把超时设置为无限。。)
到了这里,基本都讲完了,下面给出完整代码
/******************************************************************
Student Number: 3100604012
Name: Duwen
Date:2012-6-9
Type:Demonstration code
Description:Operating System Course Design (OSCD)---Thread synchronization
with semaphore. you must know this code only as a demonstration of the Windows
API , it's unwise to copy this code to OSCD report directly, you should regard this
code as a reference to you. and then write it by yourself.
*******************************************************************/#include "stdafx.h"
#include <Windows.h>
#define MAX_SEM_COUNT 10
#pragma warning(disable:4996)
//Ignore 4996 warning
typedef HANDLE OSCD_semaphore;
////////////////////////////////////////////////////////////////////////////
//Define the semaphores needed as global variables
OSCD_semaphore mutex,reader,writer;
//Define the counters
int readcount=0, writecount=0,readapp=0;
//Allocate buffer
char buffer[256]={0};
///////////////////////////////////////////////////////////////////
//Function to create and initialize a new semaphore
OSCD_semaphore OSCD_CreateSemaphore(
int nInitialCount)
{
OSCD_semaphore hSemaphore;
if (nInitialCount>MAX_SEM_COUNT){
//sanity check
printf("semaphore count too big ,please specify a value range 0 to 10\n");
exit(0);
}
hSemaphore=::CreateSemaphoreA(NULL,nInitialCount,MAX_SEM_COUNT,NULL );
if (hSemaphore == NULL) {
printf("CreateSemaphore error: %d\n", GetLastError());
exit(0);
}
return hSemaphore;
}
//Function to delete the semaphore
void OSCD_DelSemaphore( OSCD_semaphore semaphore)
{
::CloseHandle(semaphore);
}
//P operation
void P(OSCD_semaphore semaphore)
{
::WaitForSingleObject(semaphore,INFINITE);
}
//V operation
void V(OSCD_semaphore semaphore)
{
if (!::ReleaseSemaphore(semaphore,1,NULL))
{
printf("release semaphore error: %d\n",GetLastError());
exit(0);
}
}
//Reader thread function
DWORD WINAPI Reader(LPVOID lpParameter)
{
char *pThreadName=(
char *)lpParameter;
Sleep((pThreadName[6]-'0')*5);
P(mutex);
if (writecount>0||buffer[0]==NULL){
if(writecount>0)
printf("%s find there exists writer(s) trying to write , wait
![](http://www.cppblog.com/Images/dot.gif)
\n",pThreadName);
else printf("There is no data in buffer now,%s wait
![](http://www.cppblog.com/Images/dot.gif)
\n" ,pThreadName);
readapp++;
V(mutex);
P(reader);
printf("%s gets semaphore successfully !",pThreadName);
P(mutex);
readapp--;
readcount++;
if (readapp>0) V(reader);
V(mutex);
}
else{
readcount++;
V(mutex);
}
printf("%s read the data that :%s\n",pThreadName,buffer);
P(mutex);
readcount -- ;
if ( readcount == 0 && writecount>0 )
V(writer) ;
V(mutex) ;
return 0;
}
//Writer thread function
DWORD WINAPI Writer(LPVOID lpParameter)
{
char *pDataToWrite=(
char *)lpParameter;
Sleep((pDataToWrite[7]-'0')*7);
P(mutex);
writecount ++ ;
printf("A new writer is trying to write
![](http://www.cppblog.com/Images/dot.gif)
\n");
//when there are readers are reading or writers are trying to write, waiting..
if (readcount>0 || writecount>1 ){
V(mutex);
printf(" Waiting(writer) semaphore\t The contents to write is:%s\n ", pDataToWrite);
P(writer);
}
else V(mutex) ;
//skip waiting![](http://www.cppblog.com/Images/dot.gif)
int nLen=strlen(pDataToWrite);
strncpy(buffer,pDataToWrite,nLen/2);
//write
Sleep(3);
//wait 3 ms
strcpy(buffer+nLen/2,pDataToWrite+nLen/2);
P(mutex);
writecount -- ;
if (writecount>0 ) V(writer) ;
else if (readapp>0 ) V(reader) ;
printf("A writer waited gets semaphore ,Writing: \" %s \" to buffer
![](http://www.cppblog.com/Images/dot.gif)
\n ", pDataToWrite );
V(mutex) ;
return 0;
}
int _tmain(
int argc, _TCHAR* argv[])
{
//Create semaphores
mutex= OSCD_CreateSemaphore(1);
reader=OSCD_CreateSemaphore(0);
writer=OSCD_CreateSemaphore(0);
int i,j;
char *DataToWrite[6]={
"(writer1) ","(writer2)","(writer3)",
"(writer4) ","(writer5)","(writer6)"
};
char *ReaderName[6]={
"reader1","reader2","reader3","reader4","reader5","reader6"
};
HANDLE hThreads[12];
for ( i=0,j=0;i<6;i++)
{
hThreads[j]=::CreateThread(NULL,0,Reader,ReaderName[i],SW_NORMAL,NULL);
if(!hThreads[j++]){ printf("create thread failed\n") ;
return 0;}
hThreads[j]=::CreateThread(NULL,0,Writer,DataToWrite[i],SW_NORMAL,NULL);
if(!hThreads[j++]){ printf("create thread failed\n");
return 0;}
}
printf("CREATE THREADS COMPLETED\n");
//Wait for all the threads ending up.and then exit.
WaitForMultipleObjects(12,hThreads,TRUE,INFINITE);
//Delete semaphores
OSCD_DelSemaphore(mutex);
OSCD_DelSemaphore(writer);
OSCD_DelSemaphore(reader);
return 0;
}
posted on 2012-06-09 20:44
demons 阅读(1766)
评论(2) 编辑 收藏 引用 所属分类:
Operating System