昨天接到操作系統(tǒng)課程設(shè)計題目,是用信號量同步進程(線程)的,由于要用到Windows API ,好幾個同學(xué)問我API是干什么的,由于大家都沒接觸過,不知怎么用,所以我就以課設(shè)題目為例,給大家介紹一下API的一些基本知識。但請您注意,本文旨在介紹API使用,而不是給您提供現(xiàn)成的代碼讓您直接復(fù)制到課程設(shè)計報告中去,您應(yīng)該只把這做為一份參考,然后寫出自己的代碼。好了,我們從最基本的講起,首先看題目:
Linux或Windows或Unix環(huán)境下,采用系統(tǒng)調(diào)用中的信號量、P、V操作,編程解決以問題。讀者與寫者問題。一個數(shù)據(jù)集為多個并發(fā)進程所共享,其中一些進程只要求讀該數(shù)據(jù)集的內(nèi)容,這些進程稱為“讀者”,而另一些進程則要求修改該數(shù)據(jù)集的內(nèi)容,這些進程稱為“寫者”。具體要求是:允許多個讀者同時讀該數(shù)據(jù)集的內(nèi)容;若有一個寫者在寫,則其他讀者不能讀;若一個寫者在寫或有其他讀者在讀,則其他寫者均被拒絕;當(dāng)一個寫者正在寫,而有多個讀者與寫者在等待時,寫者應(yīng)優(yōu)先喚醒。請用P、V操作寫出進程的同步算法。要求打?。撼跏紶顟B(tài),中間變化的狀態(tài)信息,以及最終狀態(tài)信息。
我以Windows做為示例,考慮到大家并非都懂C++,故代碼用C寫。分析:
1.理想狀態(tài):我們程序中完全和作業(yè)中的方式一樣,比如定義信號量用 semaphore mutex; P操作就直接P(mutex),V操作就直接V(mutex),盡量避免直接使用API。
好的,我們首先實現(xiàn)這兩種功能。由于筆者平時開發(fā)習(xí)慣,都會在特定的工程中的函數(shù)、數(shù)據(jù)類型加上工程標(biāo)志,這個項目是Operating system Course Design,我就取OSCD,做為函數(shù)和數(shù)據(jù)類型前綴。所以上面的semaphore mutex;定義信號量的方式就變?yōu)镺SCD_semaphore mutex(只是換了個衣服而已)。下面介紹第一個API函數(shù)(為了先避免字符編碼的問題,看我這個不要對函數(shù)名后的A太過在意,以后接觸Windows編程后,你自然就懂了):
HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCSTR lpName );首先看一下這個中的數(shù)據(jù)類型;其實 LONG 就是typedef long LONG; LPCSTR 就是char * (字符串的指針),LPSECURITY_ATTRIBUTES 和HANDLE目前不是一兩句話能將清楚的,LPSECURITY_ATTRIBUTES 可以先直接忽略掉,你可以把HANDLE 理解為操作信號的“手柄”,也可以先理解為指向信號量的類型,以后我們對信號量的操作就是通過它來完成的,在直接一點,我們就可以把它就當(dāng)作是信號量,其中l(wèi)InitialCount就好比我們作業(yè)里mutex.value,中的value,我們需要在調(diào)用這個函數(shù)時給它賦初值,lMaximumCount是指定信號量的最大值,本例中我們?nèi)?0,最后一個參數(shù)lpName 課設(shè)用不到,直接傳NULL。回過頭來看看這個函數(shù)實現(xiàn)的功能:創(chuàng)建一個信號量并返回,在創(chuàng)建信號量時我們必須對信號量賦初值。也就是說我們真正用到的參數(shù)只有一個lInitialCount,下面我們對其封裝;
typedef HANDLE OSCD_semaphore //這樣以后就可以直接這樣 OSCD_semaphore mutex;定義信號量了;
#define MAX_SEM_COUNT 10 //信號量最大值我們?nèi)?0
OSCD_semaphore OSCD_CreateSemaphore(int nInitialCount) //參數(shù)是初值,返回的是信號量。
{
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 );//真正創(chuàng)建信號量
if (hSemaphore == NULL) {
printf("CreateSemaphore error: %d\n", GetLastError());
exit(0);
}
return hSemaphore;
}
拿本例中的 mutex 為例;我們只需要這么初始化它:OSCD_semaphore mutex;//定義mutex, mutex= OSCD_CreateSemaphore(1);//初始化;下面是刪除信號量的函數(shù):
void OSCD_DelSemaphore( OSCD_semaphore semaphore)
{
::CloseHandle(semaphore);//你可以認為這個API可以刪除掉用上面函數(shù)創(chuàng)建的信號量,要刪除那個信號量,參數(shù)就是哪個信號量,比如OSCD_DelSemaphore(mutex);
}
P操作:按照P操作的定義,先對信號量值減1,然后再和0比較,若大于等于0,就可以得到資源繼續(xù)運行,小于0的話就等待。我先給出代碼再做解釋:
void P( OSCD_semaphore semaphore)
{
::WaitForSingleObject(semaphore,INFINITE);//這個API是等待信號量的函數(shù),若等待的信號量值大于等于0時,便返回,程序繼續(xù)運行,若無資源了,便會一直等待,函數(shù)不會返回,程序就不能再往下運行
}
V操作:使信號量加1;很簡單:
void V(OSCD_semaphore semaphore)
{
if (!::ReleaseSemaphore(semaphore,1,NULL))//該API使信號量加1(第二個參數(shù),如果你想使它一次加2,只需給第二個參數(shù)傳2 即可)
{
printf("release semaphore error: %d\n",GetLastError());//檢查上面函數(shù)是否調(diào)用成功
exit(0);
}
}//該函數(shù)使用方法,還以mutex為例說明:V(mutex);
至此我們的初步目標(biāo)已經(jīng)實現(xiàn),接下來就是創(chuàng)建線程的函數(shù);
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags, Lpdword lpThread);
去枝葉剪頁,其中只有第3個和第4個參數(shù)有用,LPTHREAD_START_ROUTINE 是一個線程函數(shù)指針(其實你也可以把線程理解為特殊的函數(shù),特殊主要體現(xiàn)在它能和其它線程函數(shù)并發(fā)執(zhí)行)函數(shù)的原型是這樣的DWORD WINAPI threadfunction(LPVOID lpParameter),意思是,這個函數(shù)有一個空指針類型的參數(shù),該參數(shù)返回值是DWORD(unsigned long)型的,WINAPI 就是_stdcall 是函數(shù)的一種調(diào)用方式(如果現(xiàn)在不懂,沒關(guān)系,以后會懂的,不必糾結(jié)),第四個參數(shù)是一個空類型指針,仔細看,這個參數(shù)其實就是傳遞給線程函數(shù)的,趕緊看線程函數(shù)的參數(shù)是不是正是LPVOID lpParameter,我們可以把寫者要寫的內(nèi)容的指針放到這個參數(shù)里面?zhèn)鬟f給線程函數(shù),讓線程函數(shù)執(zhí)行時會從這個參數(shù)所指的內(nèi)容取出然后再寫進緩沖區(qū)。總結(jié)一些,也就是我們要創(chuàng)建一個線程就必須提供一個固定的函數(shù)指針(也就是函數(shù)名),然后再把要傳給線程的數(shù)據(jù)的指針給lpParameter。好了我給出調(diào)用這個函數(shù)的示例:
DWORD WINAPI Writer(LPVOID lpParameter){
寫者的具體代碼…
}
char * p="DataToWrite";
HANDLE hThreads=::CreateThread(NULL,0,Reader,p,SW_NORMAL,NULL);//只需注意第3、4個參數(shù)就行,其它的參數(shù)不要管。
這樣線程創(chuàng)建就完了;但看一下這種情況 //把創(chuàng)建線程作為最后一個語句時main函數(shù)會在創(chuàng)建完線程后退出,也就意味著程序結(jié)束,但這是線程只是創(chuàng)建成功,但并沒有執(zhí)行的機會。
int main(){
………..
HANDLE hThreads=::CreateThread(NULL,0,Reader,p,SW_NORMAL,NULL);
}
要解決這個問題,就要用到另一個API,在這個實驗中一共有多個線程,我們必須在創(chuàng)建完線程后等待它們?nèi)窟\行結(jié)束后才能讓main函數(shù)退出。
WaitForMultipleObjects( DWORD nCount, HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);這個函數(shù)中第一個參數(shù)指得是要等待線程的總數(shù)量,第二個參數(shù)是所有線程的“手柄”數(shù)組,因為等待函數(shù)是通過線程的“手柄”才能操作線程,等待函數(shù)也一樣,只有得到它的句柄才能在其內(nèi)核對象上等待;所以我們應(yīng)該這樣做:
HANDLE hThreads[12];//本例中有六個讀者線程,六個寫者線程;
然后再創(chuàng)建線程成功后把每個線程的句柄都保存在這個數(shù)組中WaitForMultipleObjects( DWORD nCount, HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);然后再這樣調(diào)用:
WaitForMultipleObjects(12,hThreads,TRUE,INFINITE); //這個函數(shù)會在所有等待的線程都執(zhí)行完成后返回,接著main函數(shù)返回,程序結(jié)束。由于這個函數(shù)也可用于等待其中任一形成結(jié)束,后返回(第三個參數(shù)傳FALSE時,意思是不必等所有線程都執(zhí)行完,只要有一個執(zhí)行完,就返回),所以我們在這傳TRUE,并且在最后一個參數(shù)(等待超時,如果在這個時間內(nèi)線程還沒有結(jié)束,該函數(shù)也會返回,所以我們應(yīng)該把超時設(shè)置為無限。。)
到了這里,基本都講完了,下面給出完整代碼
/******************************************************************
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

\n",pThreadName);
else printf("There is no data in buffer now,%s wait

\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

\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
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

\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 閱讀(1802)
評論(2) 編輯 收藏 引用 所屬分類:
Operating System