实验3同步机制.docx

上传人:b****0 文档编号:12482953 上传时间:2023-04-19 格式:DOCX 页数:29 大小:167.75KB
下载 相关 举报
实验3同步机制.docx_第1页
第1页 / 共29页
实验3同步机制.docx_第2页
第2页 / 共29页
实验3同步机制.docx_第3页
第3页 / 共29页
实验3同步机制.docx_第4页
第4页 / 共29页
实验3同步机制.docx_第5页
第5页 / 共29页
点击查看更多>>
下载资源
资源描述

实验3同步机制.docx

《实验3同步机制.docx》由会员分享,可在线阅读,更多相关《实验3同步机制.docx(29页珍藏版)》请在冰豆网上搜索。

实验3同步机制.docx

实验3同步机制

实验3同步机制

实验内容:

学习Windows有关进程/线程同步的背景知识和API,学习windows平台下常用的同步方式,并分析2个实验程序(利用信号量实现两个进程间的同步和利用互斥量实现读者写者问题),观察程序的运行情况并分析执行结果。

实验目的:

在本实验中,通过对互斥量(Mutex)和信号量(Semaphore)对象的了解,来加深对

Windows进程、线程同步的理解。

(1)了解互斥量和信号量对象。

(2)通过分析实验程序,理解管理信号量对象的API。

(3)理解在进程中如何使用信号量对象。

(4)通过分析实验程序,理解在线程中如何使用互斥量对象。

(5)理解父进程创建子进程的程序设计方法,理解在主线程中创建子线程的方法。

实验要求:

(1)理解Windows有关进程/线程同步的背景知识和API。

(2)按要求运行2个程序,观察程序执行的结果,并给出要求的结果分析。

实验选做部分:

(3)参照3-2程序,写出一个实现单个生产者—消费者问题的算法,可以使用单个缓冲

区,也可以使用缓冲池,生产者随机产生任意形式的数据并放入缓冲区中,消费者则以随机的时间间隔从缓冲区中取数据,随机时间请使用随机数产生。

并发与同步的背景知识

更多的、更详细的参考资料和代码举例在文件夹“进程线程程序设计及同步机制的学习资料”中,请利用课余时间仔细阅读。

Windows开发人员可以使用同步对象来协调线程和进程的工作,以使其共享信息并执行任务。

此类对象包括互斥量Mutex、信-号量Semaphore事件Event等。

多进程、多线程编程中关键的一步是保护所有的共享资源,工具主要有互斥量Mutex

和信号量Semaphore等;另一个是协调线程使其完成应用程序的任务,为此,可利用内核中

的信号量对象或事件对象。

互斥量是一个可命名且安全的内核对象,主要目的是引导对共享资源的访问。

拥有单一访问资源的线程创建互斥体,所有希望访问该资源的线程应该在实际执行操作之前获得互斥体,而在访问结束时立即释放互斥体,以允许下一个等待线程获得互斥体,然后接着进行下去。

利用CreateMutex()API可创建互斥量,创建时可以指定一个初始的拥有权标志,通过使用这个标志,只有当线程完成了资源的所有的初始化工作时,才允许创建线程释放互斥体。

为了获得互斥体,首先,想要访问调用的线程可使用OpenMutex()API来获得指向对象

的句柄;然后,线程将这个句柄提供给一个等待函数。

当内核将互斥体对象发送给等待线程时,就表明该线程获得了互斥体的拥有权。

当线程获得拥有权时,线程控制了对共享资源的访问——必须设法尽快地放弃互斥体。

放弃共享资源时需要在该对象上调用ReleaseMutex()

API。

然后系统负责将互斥量拥有权传递给下一个等待着的线程(由到达时间决定顺序)。

信号量Semaphore与互斥量Mutex的用法不同,互斥量Mutex保证任意时刻只能有一个进程或线程获得互斥体,信号量允许多个线程同时使用共享资源,这与操作系统中的Wait/Signal操作【也称PV操作】相同。

它指出了同时访问共享资源的线程最大数目。

允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。

在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可

用资源计数。

一般是将当前可用资源计数设置为最大资源计数或者0。

每增加一个线程对共

享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发

出信号量信号。

但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。

线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。

任何时候当前可用资源计数决不可能大于最大资源计数。

此外,windows还提供了另外一个容易误导大家理解的同步对象,取名为Critical

Section,中文取名为临界区,请大家注意与课本讲的临界资源、临界区的概念相区分。

课本上临界区指“对临界资源进行访问的代码”;而这种称为“CriticalSection”互斥机制,并不是这个意思,而是访问临界区之前的一种加锁机制,与互斥量Mutex的作用类似,只是

“CriticalSection”互斥机制只能在同一进程内部各个线程间使用,而Mutex互斥机制是可

以跨进程使用的。

实验内容与步骤

1.信号量Semaphore对象

清单3-1程序展示如何在进程间使用信号量对象。

父进程启动时,利用CreateSemaphore()API创建一个命名的、可共享的信号量对象,并利用CreateProcess(创建子进程,然后调用WaitForSingleObject()函数去获取信号量,

但是由于信号量的初始值为0,所以父进程阻塞在此,无法成功占有信号量;子进程创建后,调用OpenSemaphore()打开父进程创建的信号量,然后调用ReleaseSemaphore()函数释

放了1个信号量,此后,处于阻塞状态的父进程获取了子进程释放的信号量,从而解除了阻塞,继续运行至程序结束。

清单3-1创建和打开信号量对象在进程间传送信号

//Semaphore信号量项目

#include

#include

#include

//定义一个信号量的名字

staticLPCTSTRg_szSemaphoreName="shmtu.os2012.semaphore";

//本方法只是创建了一个进程的副本,以子进程模式(由命令行指定)工作

BOOLCreateChild()

{

//提取当前可执行文件的文件名TCHARszFilename[MAX_PATH];

GetModuleFileName(NULL,szFilename,MAX_PATH);

//格式化用于子进程的命令行,指明它是一个EXE文件和子进程TCHARszCmdLine[MAX_PATH];

sprintf(szCmdLine,"\"%s\"child",szFilename);

//子进程的启动信息结构STARTUPINFOsi;

ZeroMemory(reinterpret_cast(&si),sizeof(si));si.cb=sizeof(si);//必须是本结构的大小

//返回的子进程的进程信息结构PROCESS_INFORMATIONpi;

//使用同一可执行文件和告诉它是一个子进程的命令行创建进程

BOOLbCreateOK=szFilename,szCmdLine,

NULL,

NULL,FALSE,

CreateProcess(

//生成的可执行文件名

//指示其行为与子进程一样的标志

//子进程句柄的安全性

//子线程句柄的安全性

//不继承句柄

CREATE_NEW_CONSOLE,//特殊的创建标志

NULL,

NULL,&si,&pi);

//新环境

//当前目录

//启动信息结构

//返回的进程信息结构

//释放对子进程的引用

if(bCreateOK)

{

CloseHandle(pi.hProcess);CloseHandle(pi.hThread);

}return(bCreateOK);

}

//下面的方法创建一个信号量和一个子进程,然后等待子进程在返回前释放一个信号voidWaitForChild()

{

HANDLEhSemaphore=CreateSemaphore(//创建一个信号量,初始值0,最大值为1NULL,//缺省的安全性,子进程将具有访问权限

0,//初始值0

1,//最大值为1g_szSemaphoreName//信号量名称);

if(hSemaphore!

=NULL)

{

cout<<"信号量对象已经建立啦。

"<

//创建子进程

if(CreateChild())

{

cout<<"父进程建立了一个子进程"<

//等待,直到子进程发出信号

cout<<"因为当前信号量的值为0,所以父进程暂时阻塞在此啦."<

WaitForSingleObject(hSemaphore,INFINITE);

cout<<"因为子进程释放了1个信号量,所以,父进程可以解除阻塞啦。

"<

//清除句柄

CloseHandle(hSemaphore);hSemaphore=INVALID_HANDLE_VALUE;

}

}

//以下方法在子进程模式下被调用,其功能只是释放1个信号量

voidSignalParent()

{

//尝试打开句柄

cout<<"childprocessbegining"<

HANDLEhSemaphore=OpenSemaphore(SEMAPHORE_ALL_ACCESS,//所要求的最小访问权限FALSE,//不是可继承的句柄

g_szSemaphoreName);//信号量名称

if(hSemaphore!

=NULL)

{

cout<<"如果你同意释放一个信号量,请按任意键"<

cout<<"此刻,子进程释放了1个信号量."<

}

//清除句柄

CloseHandle(hSemaphore);hSemaphore=INVALID_HANDLE_VALUE;

}

intmain(intargc,char*argv[])

{

//检查父进程或是子进程是否启动

if(argc>1&&strcmp(argv[1],"child")==0)

{

//向父进程发出信号SignalParent();cout<<"子进程马上就要运行结束了。

请按任意键继续。

"<

}

else

{

//创建一个信号量并等待子进程WaitForChild();

cout<<"父进程马上就要运行结束了。

请按任意键继续。

"<

}

getchar();return0;

}

步骤1:

编译并执行3-1.exe程序。

程序运行结果是(分行书写):

1

2

3

4

5

6

阅读和分析程序3-1,请回答:

(1)程序中,创建一个信号量使用了哪一个系统函数?

创建时设置的信号量初始值是多少,最大值是多少?

a.

b.

(2)创建一个进程(子进程)使用了哪一个系统函数?

(3)从步骤1的输出结果,对照分析3-1程序,能够看出程序运行的流程吗?

请简单描述:

步骤2:

编译程序生成执行文件3-1.exe,在命令行状态下执行程序,分别使用格式

⑴3-1child

(2)3-1或3-1***

运行程序,记录执行的结果,并分行说明产生不同结果的原因。

2.互斥量Mutex对象

注意:

多线程编程需要在VisualC++中设定多线程C运行时库。

打开你的工程项目后,在VisualC++的"Project”菜单下面,选择"Settings”菜单,如下两图所示,可以分别设定Debug版本和Release版本所使用的多线程C运行时库,以Debug开头的都是为Debug

版本准备的,都选Multithread版本。

AlfFT

DFS5_h>

 

4

zJ«

adl-Line

fA*-

NLdfWl阳IB/tiX^3/Dd/U■WNUIT/\3

-DEBIJIT柏,fcMBCST

jFjj'Ti^hutreenfciph+^eximtie.f^jnw斤解■DEuaTw

prlBtrihfhrp^

如果使用标准C库而调用VC运行时库函数,则在程序的link阶段会提示如下错误:

errorLNK2001:

unresolvedexternalsymbol__endthreadexerrorLNK2001:

unresolvedexternalsymbol__beginthreadex

清单3-2的程序中是读者写者问题的一个实现,满足读者优先原则,使用同步机制的互

斥量实现,对每个读者和写者分别用一个线程来表示。

测试数据文件的数据格式说明:

测试数据文件包括n行测试数据,每行描述创建的是用于产生读者还是写者的数据。

行测试数据包括4个字段,各字段间用空格分隔。

第一字段为线程序号。

第二字段表示相应线程角色,W表示写者,R表示读者。

第三字段为线程延迟。

第四字段为线程读写操作持续时间。

HANDLERP_Write;

〃等待互斥变量所有权

//延迟时间

//读文件持续时间

//线程序号

unsignedint__stdcallRP_ReaderThread(void*p)

{—-

//互斥变量

HANDLEh_Mutex;

h_Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex_for_readcount");

DWORDwait_for_mutex;

DWORDm_delay;

DWORDm_persist;

intm_serial;

//从参数中获得信息

m_serial=((Threadinfo*)(p))->serial;

m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);//延迟等待

printf("Readerthread%dsentsthereadingrequire\n",m_serial);

//等待互斥信号,保证对readcount的访问、修改互斥wait_for_mutex=WaitForSingleObject(h_Mutex,-1);

//读者数目增加readcount++;if(readcount==1)

{

//这是第一个读者,第二个读者到来时,readcount为2,if的条件不满足,不会进入if语句内部执行

//这是第一个读者,如果此刻没有写者正在写,则RP_Write信号量状态为可用(未占用),那

它就必须先占用(锁定)RP_Write信号量,这就实现了读-写互斥

//如果此刻有写者正在写,则RP_Write信号量被写者占用(锁定),读者想对RP_Write加锁就会阻塞在此,等待RP_Write信号量释放后,才能继续运行

WaitForSingleObject(RP_Write,INFINITE);

}

ReleaseMutex(h_Mutex);//释放互斥信号

//读文件

printf("Readerthread%dbeginstoreadfile.\n",m_serial);

Sleep(m_persist);

//退出线程

printf("Readerthread%dfinishedreadingfile.\n",m_serial);

//等待互斥信号,保证对readcount的访问、修改互斥

wait_for_mutex=WaitForSingleObject(h_Mutex,-1);

//读者数目减少

readcount--;if(readcount==0)

{//如果所有读者读完,则释放RP_Write信号量;此刻若有写者正在等待(处于阻塞状态),将

(自动)唤醒写者

ReleaseMutex(RP_Write);

}

ReleaseMutex(h_Mutex);//释放互斥信号

return0;

}////////////////////////////////////////////////////////////////////////

//读者优先——写者线程

//p:

写者线程信息

unsignedint__stdcallRP_WriterThread(void*p)

{

DWORDm_delay;//延迟时间

DWORDm_persist;//写文件持续时间

intm_serial;//线程序号

//从参数中获得信息

m_serial=((ThreadInfo*)(p))->serial;

m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);

Sleep(m_delay);//延迟等待

printf("Writerthread%dsentsthewritingrequire.\n",m_serial);

//写者在进行写数据之前,必须确定没有其它写者正在写,也没有其它读者在读,

//通过RP_Write互斥量实现,若有其它写者正在写或其它读者在读,则该写者调用“WaitForSingleObject(RP_Write,INFINITE);”后将阻塞在此

//等待RP_Write信号量释放后,才能够继续向下执行。

WaitForSingleObject(RP_Write,INFINITE);

//写文件

printf("Writerthread%dbeginstowritetothefile.\n",m_serial);

Sleep(m_persist);

//退出线程

printf("Writerthread%dfinishedwritingtothefile.\n",m_serial);

//写者写完后,需要释放RP_Write信号量资源ReleaseMutex(RP_Write);

return0;

}///////////////////////////////////////////////////////////

//读者优先处理函数

//file:

文件名

voidReaderPriority(char*file)

{

inti;

DWORDn_thread=0;//线程数目

UINTthread_ID;//线程ID

DWORDwait_for_all;//等待所有线程结束

//互斥对象

HANDLEh_Mutex;

h_Mutex=CreateMutex(NULL,FALSE,"mutex_for_readcount");

//线程对象的数组

HANDLEh_Thread[MAX_THREAD_NUM];

ThreadInfothread_info[MAX_THREAD_NUM];

readcount=0;//初始化readcountt

RP_Write=CreateMutex(NULL,FALSE,NULL);//初始化用于读-写互斥、写-写互斥的信号量

ifstreaminFile;//打开文件

inFile.open(file);

printf("ReaderPriority:

\n\n");

while(inFile)

{

//读人每一个读者、写者的信息inFile>>thread_info[n_thread].serial;

inFile>>thread_info[n_thread].entity;

inFile>>thread_info[n_thread].delay;inFile>>thread_info[n_thread++].persist;

inFile.get();

}

for(i=0;i<(int)(n_thread);i++)

{

if(thread_info[i].entity==READER||thread_info[i].entity=='r')

{

//创建读者线程

h_Thread[i]=(HANDLE)_beginthreadex(NULL,0,RP_ReaderThread,&thread_info[i],0,&thread_ID);

}

else{

//创建写者线程

h_Thread[i]=(HANDLE)_beginthreadex(NULL,0,RP_WriterThread,&thread_info[i],0,

&thread_ID);

}

//等待所有线程结束

wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);printf("Allreaderandwriterhavefinishedoperating.\n");

//关闭线程句柄,关闭信号量句柄

for(i=0;i<(int)(n_thread);i++){CloseHandle(h_Thread[i]);}CloseHandle(h_Mutex);

CloseHandle(RP_Write);

}

///////////////////////////////////////////////////////////////////

//主函数intmain(intargc,char*argv[])

charch;

while(true)

{

printf("1:

ReaderPriority\n");

printf("2:

ExittoWindows\n");

printf("*********************************printf("Enteryourchoice(1or2):

");

//如果输入信息不正确,继续输入do{

ch=(char)_getch();

}while(c

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 艺术创意

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1