资源预览内容
第1页 / 共111页
第2页 / 共111页
第3页 / 共111页
第4页 / 共111页
第5页 / 共111页
第6页 / 共111页
第7页 / 共111页
第8页 / 共111页
第9页 / 共111页
第10页 / 共111页
亲,该文档总共111页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第二章 Windows多线程编程内容1.Windows操作系统的一些基本知识操作系统的一些基本知识2.Win32API线程库线程库3.线程间通信线程间通信1、Windows操作系统的一些基本知识操作系统的一些基本知识1.API2.内核对象及句柄内核对象及句柄API ( Application Programming Interface )nAPIAPI:操作系统留给应用程序的一个调用接口,应操作系统留给应用程序的一个调用接口,应用程序通过用程序通过APIAPI使操作系统去执行使操作系统去执行相应相应程序程序。nWindows APIWindows API是一套用来控制是一套用来控制WindowsWindows的各个部件的各个部件的外观和行为的预先定义的的外观和行为的预先定义的WindowsWindows函数。函数。nWin32 APIWin32 API即为即为Microsoft 32Microsoft 32位平台的应用程序编位平台的应用程序编程接口。程接口。Win32Win32平台上运行的应用程序都可以调用平台上运行的应用程序都可以调用这些函数。这些函数。n3232与与6464位位APIAPI函数声明上没有明显区别,但函数声明上没有明显区别,但6464位编位编程是需要下载相应的程是需要下载相应的软件开发包软件开发包SDKSDK。 API ( Application Programming Interface )n直接用直接用win32 APIwin32 API编写的应用程序,程序的执行代编写的应用程序,程序的执行代码小,运行效率高码小,运行效率高nMFCMFC用类库的方式将用类库的方式将 win32 API win32 API 进行封装进行封装, , 以类以类的方式提供给开发者的方式提供给开发者内核对象及句柄n内核对象是由操作系统内核分配,内核对象是由操作系统内核分配,只能由内核访只能由内核访问的数据结构问的数据结构,供系统和应用程序使用来管理各供系统和应用程序使用来管理各种系统资源种系统资源。n内核对象包括:内核对象包括:进程对象、线程对象、事件对象、进程对象、线程对象、事件对象、文件对象、作业对象、互斥对象、等待计时器对文件对象、作业对象、互斥对象、等待计时器对象等都是内核对象。象等都是内核对象。n出于安全的考虑,进程不能直接访问内核对象。出于安全的考虑,进程不能直接访问内核对象。n操作系统提供了一组函数来访问内核对象。通过操作系统提供了一组函数来访问内核对象。通过函数创建、打开和操作内核对象。函数创建、打开和操作内核对象。内核对象及句柄n内核对象由内核拥有,各个进程可以共享内核对内核对象由内核拥有,各个进程可以共享内核对象。进程终止执行,它使用的内核对象并不一定象。进程终止执行,它使用的内核对象并不一定会被撤销。会被撤销。n每个内核对象都有一个计数器来存储有多少个进每个内核对象都有一个计数器来存储有多少个进程在使用它的信息。程在使用它的信息。n进程调用时,计数器增进程调用时,计数器增1,调用结束,计数器减,调用结束,计数器减1。n内核对象计数器为零时,销毁此内核对象。内核对象计数器为零时,销毁此内核对象。内核对象及句柄n内核对象有安全描述符的保护,安全描述符描述内核对象有安全描述符的保护,安全描述符描述了谁创建了该对象以及谁能够使用该对象。了谁创建了该对象以及谁能够使用该对象。n用于创建内核对象的函数几乎都有一个指向用于创建内核对象的函数几乎都有一个指向SECURITY_ATTRIBUTES结构的指针作为其参结构的指针作为其参数。数。n大多数应用程序通过传大多数应用程序通过传NULL值,创建具有默认值,创建具有默认安全性的对象。如果想限制其他线程对对象的访安全性的对象。如果想限制其他线程对对象的访问,就需要单独创建一个问,就需要单独创建一个SECURITY_ATTRIBUTES对象并对其初始化。对象并对其初始化。内核对象及句柄n句柄:句柄:创建内核对象时,函数的返回值,标记该创建内核对象时,函数的返回值,标记该内核对象。内核对象。n句柄表:句柄表:进程被初始化时,系统给进程分配一个进程被初始化时,系统给进程分配一个句柄表,用于保存该进程使用的内核对象的信息,句柄表,用于保存该进程使用的内核对象的信息,而句柄值则是相应内核对象在句柄表中的索引值,而句柄值则是相应内核对象在句柄表中的索引值,因此句柄值是进程相关的。因此句柄值是进程相关的。内核对象及句柄n内核对象创建内核对象创建n当利用当利用creat*函数来创建内核对象时,系统内核就为该函数来创建内核对象时,系统内核就为该对象分配一个内存块,并进行初始化,然后系统内核扫对象分配一个内存块,并进行初始化,然后系统内核扫描该进程的句柄表,初始化一条记录并放在句柄表中。描该进程的句柄表,初始化一条记录并放在句柄表中。n关闭内核对象关闭内核对象n无论进程怎样创建内核对象,在不使用该对象的时候都无论进程怎样创建内核对象,在不使用该对象的时候都应当通过应当通过BoolCloseHandle(HANDLEhobj)来向操作来向操作统声明结束对该对象的访问。统声明结束对该对象的访问。Win32API线程库线程库1.创建线程的基本问题创建线程的基本问题2.创建线程的创建线程的API函数函数3.操作线程的操作线程的API4.一个简单的一个简单的Windows多线程程序多线程程序1 创建线程的基本问题 n线程可以由进程中的任意线程创建,而进程的主线程在进线程可以由进程中的任意线程创建,而进程的主线程在进程加载时自动创建。程加载时自动创建。n每个线程都有自己的进入点函数。每个线程都有自己的进入点函数。n主线程的进入点函数主线程的进入点函数进入点进入点应用程序类型应用程序类型WinMainWinMain需要需要ANSIANSI字符和字符串的字符和字符串的GUIGUI应用程序应用程序wWinMainwWinMain需要需要UnicodeUnicode字符和字符串的字符和字符串的GUIGUI应用程序应用程序MainMain需要需要ANSIANSI字符和字符串的字符和字符串的CUICUI应用程序应用程序WmainWmain需要需要UnicodeUnicode字符和字符串的字符和字符串的CUICUI应用程序应用程序线程函数的返回值是该线程的退出代码线程函数的返回值是该线程的退出代码线程函数应尽可能使用函数参数和局部变量线程函数应尽可能使用函数参数和局部变量线程函数-线程的入口点DWORDWINAPIThreadFunc(PVOIDpvParam)DWORDdwResult=0;return(dwResult);2 创建线程的API函数n创建线程过程:创建线程过程:n系统创建一个线程内核对象。线程内核对象不系统创建一个线程内核对象。线程内核对象不是线程本身,而是操作系统用来管理线程的较是线程本身,而是操作系统用来管理线程的较小的数据结构。小的数据结构。n在进程的地址空间分配内存,供线程的堆栈使在进程的地址空间分配内存,供线程的堆栈使用用HANDLECreateThread(PSECURITY_ATTRIBUTESpsa,DWORDcbStack,PTHREAD_START_ROUTINEpStartAddr,PVOIDpvParam,DWORDfdwCreate,PDWORDpdwThreadId);2 创建线程的API函数NULL0函数地址函数地址函数参数函数参数NULL控制创建线程标志控制创建线程标志CREATE_SUSPENDED0线程线程ID#includewindows.h#includeusingnamespacestd;DWORDWINAPIThreadFunc(PVOIDpvParam)coutCreatedthreadsayshelloWorld!endl;return0;intmain()HANDLEThreadHandle=CreateThread(NULL,0,ThreadFunc,NULL,0,NULL);Sleep(100);coutMainthreadsaysHelloWorld!endl;getchar();return0;n暂停线程暂停线程DWORD DWORD SuspendThread(HANDLESuspendThread(HANDLE hThreadhThread) )n返回值是线程的前一个暂停计数返回值是线程的前一个暂停计数n暂停计数:是线程内核对象的一个内部值。暂停计数:是线程内核对象的一个内部值。n使用要小心,因为不知道暂停线程运行时它在进使用要小心,因为不知道暂停线程运行时它在进行什么操作。可能造成死锁行什么操作。可能造成死锁3 操作线程的API3 操作线程的APIn恢复线程恢复线程DWORD DWORD ResumeThread(HANDLEResumeThread(HANDLE hThreadhThread); ); n返回值是线程的前一个暂停计数返回值是线程的前一个暂停计数n该函数用于将处于暂停状态的线程置于就该函数用于将处于暂停状态的线程置于就绪状态,使其参加线程调度。绪状态,使其参加线程调度。3 操作线程的APIn使线程睡眠使线程睡眠VOID Sleep (DWORD VOID Sleep (DWORD dwMillisecondsdwMilliseconds ); );n该函数是线程暂停自己的运行,直到睡眠时间过该函数是线程暂停自己的运行,直到睡眠时间过去为止。去为止。n当线程调用这个函数时,它自动放弃剩余的时间当线程调用这个函数时,它自动放弃剩余的时间片,迫使系统进行线程调度。片,迫使系统进行线程调度。nWindowsWindows不是实时的操作系统。不是实时的操作系统。3 操作线程的APIn终止线程终止线程线程函数返回(最好)线程函数返回(最好)通过调用通过调用ExitThreadExitThread函数,线程将自行撤销函数,线程将自行撤销同一个进程或另一个进程中的线程调用同一个进程或另一个进程中的线程调用TerminateThreadTerminateThread函数函数包含线程的进程终止包含线程的进程终止线程返回函数线程返回函数n线程中创建的线程中创建的C+C+类对象能够正常撤销;类对象能够正常撤销;n操作系统将正确地释放线程堆栈使用的内操作系统将正确地释放线程堆栈使用的内存;存;n系统将线程的退出代码设置为线程函数的系统将线程的退出代码设置为线程函数的返回值;返回值;n系统将递减线程内核对象的使用计数。系统将递减线程内核对象的使用计数。n线程调用这个函数,强制线程终止运行;线程调用这个函数,强制线程终止运行;n操作系统清除该线程使用的所有系统资源。操作系统清除该线程使用的所有系统资源。nC+C+类对象将不被撤销。类对象将不被撤销。VOID VOID ExitThread(DWORDExitThread(DWORD dwExitCodedwExitCode); ); ExitThreadExitThread函数函数n能够撤销任何线程;能够撤销任何线程;n线程的内核对象的使用计数也被递减;线程的内核对象的使用计数也被递减;n异步运行的函数;异步运行的函数;n不撤销线程的堆栈,直到进程终止。不撤销线程的堆栈,直到进程终止。BOOL BOOL TerminateThread(HANDLETerminateThread(HANDLE hThread,DWORDhThread,DWORD dwExitCodedwExitCode); ); TerminateThreadTerminateThread函数函数在进程终止运行时撤销线程nExitProcessExitProcess 和和 TerminateProcessTerminateProcess函数可以终止线程,函数可以终止线程,将会终止进程中的所有线程;将会终止进程中的所有线程;nExitProcessExitProcess只能强制执行本进程的退出;只能强制执行本进程的退出;nTerminateProcessTerminateProcess在一个进程中强制结束其他的进程;在一个进程中强制结束其他的进程; n进程所使用的资源被清除;进程所使用的资源被清除;nC+C+对象撤销函数没有被调用。对象撤销函数没有被调用。VOID VOID ExitProcess(UINTExitProcess(UINT uExitCodeuExitCode); ); BOOL BOOL TerminateProcess(HANDLETerminateProcess(HANDLE hProcesshProcess, UINT , UINT uExitCodeuExitCode); ); #include#includeusingnamespacestd;DWORDWINAPIFunOne(LPVOIDparam)int*p=(int*)param;cout(*p)endl;while(true)Sleep(1000);couthello!;return0;DWORDWINAPIFunTwo(LPVOIDparam)int*p=(int*)param;cout(*p)endl;while(true)Sleep(1000);coutinput;if(input=1)ResumeThread(hand1);ResumeThread(hand2);elseSuspendThread(hand1);SuspendThread(hand2);TerminateThread(hand1,1);TerminateThread(hand2,1);return0;例例n打印出打印出1001000之间的所有之间的所有“水仙花数水仙花数”,所谓,所谓“水仙花数水仙花数”是指一个三位数,其是指一个三位数,其各位数字立方和等于该数本身。各位数字立方和等于该数本身。n例如:例如:153是一个是一个“水仙花数水仙花数”,因为,因为153=13+33+53n不需要通信不需要通信intmain()inti,j,k,n;printf(水仙花数水仙花数是是:);for(n=100;nlow;inthigh=bou-high;for(n=low;nhigh;n+)i=n/100;j=n/10-i*10;k=n%10;if(i*100+j*10+k=i*i*i+j*j*j+k*k*k)printf(%d,n);return0;intmain()printf(水仙花数水仙花数是是:);boundqw1,qw2;qw1.low=100;qw1.high=500;HANDLEThreadHandle1=CreateThread(NULL,0,Thread1,&qw1,0,NULL);qw2.low=500;qw2.high=1000;HANDLEThreadHandle2=CreateThread(NULL,0,Thread1,&qw2,0,NULL);HANDLEThreadHandles2=ThreadHandle1,ThreadHandle2;WaitForMultipleObjects(2,ThreadHandles,TRUE,INFINITE);return0;线程间通信n操作系统随机调度线程,程序员不能预知线程的执行顺序操作系统随机调度线程,程序员不能预知线程的执行顺序n下面两种情况下,线程间需要通信下面两种情况下,线程间需要通信n当有多个线程访问共享资源而不希望共享资源遭到破当有多个线程访问共享资源而不希望共享资源遭到破坏;(互斥)坏;(互斥)n当一个线程需要将某个任务已经完成的情况通知另外当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时。(同步)一个或多个线程时。(同步)nWindowsWindows线程通信方法主要有互锁函数、临界段、线程通信方法主要有互锁函数、临界段、事件、事件、互斥量、信号量互斥量、信号量线程间通信1.1.互锁函数互锁函数2.2.临界段临界段3.3.事件事件4.4.互斥量互斥量5.5.信号量信号量使用内核对象的线程间通信n互锁函数和临界段都是在用户态实现线程通信的,互锁函数和临界段都是在用户态实现线程通信的,优点速度快优点速度快n用户态机制只能实现同一进程内线程通信。用户态机制只能实现同一进程内线程通信。n内核对象机制可以实现不同进程内线程的通信,缺内核对象机制可以实现不同进程内线程的通信,缺点速度慢。点速度慢。n包含通知状态和未通知状态内核属性包含通知状态和未通知状态内核属性的内核对象有:的内核对象有:n进程,线程进程,线程,作业,文件,控制台输入,作业,文件,控制台输入n文件修改通知,事件,可等待定时器文件修改通知,事件,可等待定时器n信号量,互斥量信号量,互斥量n等待函数:使线程进入等待状态等待函数:使线程进入等待状态, ,直到一直到一个对象变为已通知状态。个对象变为已通知状态。DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);参数参数dwMillisecondsdwMilliseconds有两个特殊值:有两个特殊值:0 0,则该函数立即返回;,则该函数立即返回;INFINITEINFINITE,则线程被挂起,直到,则线程被挂起,直到hHandlehHandle所指向的对所指向的对象变为已通知状态。象变为已通知状态。 使用内核对象的线程间通信n等待函数:等待函数:可以保证线程的同步可以保证线程的同步 。DWORDWaitForMultipleObject(DWORDdwCount,CONSTHANDLE*phHandle,BOOLfWaitAll,DWORDdwMilliseconds);使用内核对象的线程间通信1、互锁函数n互锁函数是用来解决原子访问的,主要针对变量互锁函数是用来解决原子访问的,主要针对变量的原子访问;的原子访问;n原子访问:当线程访问资源时,能够确保没有其原子访问:当线程访问资源时,能够确保没有其它线程同时访问相同的资源。它线程同时访问相同的资源。Longg_x=0;/全局变量全局变量DWORDWINAPIThreadFunc1(PVOIDpvParam)g_x+;return0;DWORDWINAPIThreadFunc2(PVOIDpvParam)g_x+;return0;MOVEAX,g_xINCEAXMOVg_x,EAX递增以原子方式运行递增以原子方式运行1、互锁函数(例)1、互锁函数LONGInterlockedExchangeAdd()(PLONGplAddend,LONGlIncrement);Longg_x=0;/全局变量全局变量DWORDWINAPIThreadFunc1(PVOIDpvParam)InterlockedExchangeAdd(&g_x,1);return0;DWORDWINAPIThreadFunc2(PVOIDpvParam)InterlockedExchangeAdd(&g_x,1);return0;1、互锁函数n以原子操作方式用第二个参数的值取代第以原子操作方式用第二个参数的值取代第一个参数的当前值。一个参数的当前值。LONGInterlockedExchange()(PLONGplTarget,LONGlValue);LONGInterlockedExchangePointer()(PVOID*ppvTarget,PVOIDpvValue);1、互锁函数n比较第一个参数所指的值和第三个参数的值,如比较第一个参数所指的值和第三个参数的值,如果相等,则将第一个参数所指的值置为第二个参果相等,则将第一个参数所指的值置为第二个参数,如果不相等则不进行任何操作。数,如果不相等则不进行任何操作。LONG LONG InterlockedCompareExchangeInterlockedCompareExchange ()( ()( PLONG PLONG plDestinationplDestination, LONG , LONG lExchangelExchange, LONG , LONG lComparandlComparand););LONG LONG InterlockedCompareExchangePointerInterlockedCompareExchangePointer ()( ()( PVOID* PVOID* ppvDestinationppvDestination, PVOID , PVOID pvExchangepvExchange, PVOID , PVOID pvComparandpvComparand););例:例:10000个个2相加相加intmain()intsum=0;for(inti=1;i=10000;i+)sum=sum+2;printf(10000个个2相加之和是相加之和是%d,sum);getchar();return0;#includewindows.hlongsum=0;DWORDWINAPIThread1(PVOIDpvParam)for(inti=1;i=5000;i+)/InterlockedExchangeAdd(&sum,2);sum=sum+2;return0;DWORDWINAPIThread2(PVOIDpvParam)for(inti=5001;i=10000;i+)/InterlockedExchangeAdd(&sum,2);sum=sum+2;return0;intmain()HANDLEThreadHandle1=CreateThread(NULL,0,Thread1,NULL,0,NULL);HANDLEThreadHandle2=CreateThread(NULL,0,Thread2,NULL,0,NULL);HANDLEThreadHandles2=ThreadHandle1,ThreadHandle2;WaitForMultipleObjects(2,ThreadHandles,TRUE,INFINITE);printf(10000个个2相加之和是相加之和是%d,sum);getchar();return0;2、临界段n互锁函数:以原子操作方式修改单个值互锁函数:以原子操作方式修改单个值n临界段:以原子方式修改复杂的数据结构。临界段:以原子方式修改复杂的数据结构。n临界段:关键代码段,是指一小段代码,同临界段:关键代码段,是指一小段代码,同一个时刻,只能有一个线程具有访问权。一个时刻,只能有一个线程具有访问权。n多个线程访问同一个临界区的原则:多个线程访问同一个临界区的原则:n一次最多只能一个线程停留在临界区内;一次最多只能一个线程停留在临界区内;n不能让一个线程无限地停留在临界区内,否则不能让一个线程无限地停留在临界区内,否则其它线程将不能进入该临界区其它线程将不能进入该临界区2、临界段 相关API函数 n首先定义一个临界段对象(通常全局变量)首先定义一个临界段对象(通常全局变量)CRITICAL_SECTION CRITICAL_SECTION cscsn临界段对象初始化临界段对象初始化InitializeCriticalSectionInitializeCriticalSection (& (&cscs) )n进入临界段进入临界段EnterCriticalSectionEnterCriticalSection (& (&cscs) )n离开临界段离开临界段LeaveCriticalSectionLeaveCriticalSection (& (&cscs) )n释放临界段对象释放临界段对象DeleteCriticalSectionDeleteCriticalSection (& (&cscs) )临界段 例1#include#includefstreamfile;DWORDWINAPIThreadFunc1(PVOIDparam)for(inti=1;i=1000;i+)fileThreadFunc1Outputiendl;return0;DWORDWINAPIThreadFunc2(PVOIDparam)for(inti=1;i=1000;i+)fileThreadFunc2Outputiendl;return0;intmain()file.open(data.txt,ios:out);HANDLEThreadHandle1=CreateThread(NULL,0,ThreadFunc1,NULL,0,NULL);HANDLEThreadHandle2=CreateThread(NULL,0,ThreadFunc2,NULL,0,NULL);HANDLEhThread2=ThreadHandle1,ThreadHandle2;WaitForMultipleObjects(2,hThread,TRUE,INFINITE);file.close();return0;加上临界段#include#includefstreamfile;CRITICAL_SECTIONcs;DWORDWINAPIThreadFunc1(PVOIDparam)for(inti=1;i=1000;i+)EnterCriticalSection(&cs);fileThreadFunc1Outputiendl;LeaveCriticalSection(&cs);return0;DWORDWINAPIThreadFunc2(PVOIDparam)for(inti=1;i=1000;i+)EnterCriticalSection(&cs);fileThreadFunc2Outputiendl;LeaveCriticalSection(&cs);return0;#include“windows.h“例例2CRITICAL_SECTIONg_cs;charg_cArray10;/共享共享资源源DWORDWINAPIThreadProc1(PVOIDpParam)EnterCriticalSection(&g_cs);/对共享共享资源源进行写入操作行写入操作for(inti=0;i10;i+)g_cArrayi=a;Sleep(1);LeaveCriticalSection(&g_cs);return0;DWORDWINAPIThreadProc2(PVOIDpParam)/进入入临界区界区EnterCriticalSection(&g_cs);/对共享共享资源源进行写入操作行写入操作for(inti=0;i10;i+)g_cArray10-i-1=b;Sleep(1);/离开离开临界区界区LeaveCriticalSection(&g_cs);return0;intmain()InitializeCriticalSection(&g_cs);HANDLEThreadHandle1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);HANDLEThreadHandle2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);HANDLEThreadHandles2=ThreadHandle1,ThreadHandle2;WaitForMultipleObjects(2,ThreadHandles,TRUE,INFINITE);DeleteCriticalSection(&g_cs);printf(g_cArray);getchar();return0;3、事件n事件内核对象是最简单的对象。事件内核对象是最简单的对象。n一个使用计数一个使用计数n一个布尔值,指明该事件是自动复位事件一个布尔值,指明该事件是自动复位事件(false)(false),还是人工复位事件,还是人工复位事件(true)(true);n一个布尔值,指明该事件是已通知状态一个布尔值,指明该事件是已通知状态(true)(true),还是未通知状态,还是未通知状态(false)(false) 。 n当人工复位事件得到通知时,等待该事件的所有当人工复位事件得到通知时,等待该事件的所有线程均变为可调度事件;线程均变为可调度事件;n当自动复位事件得到通知时,等待该事件的线程当自动复位事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。中只有一个线程变为可调度线程。 n创建事件内核对象,返回句柄。创建事件内核对象,返回句柄。 HANDLE HANDLE CreateEventCreateEvent( ( PSECURITY_ATTRIBUTES PSECURITY_ATTRIBUTES psapsa, / , / 安全属性安全属性 BOOL BOOL fManualResetfManualReset, /, /复位方式复位方式 BOOL BOOL fInitialStatefInitialState, / , / 初始状态初始状态 PCTSTR PCTSTR pszNamepszName / / 对象名称对象名称 ); ); 3、事件n打开一个已经存在的命名事件对象打开一个已经存在的命名事件对象 HANDLE HANDLE OpenEventOpenEvent( ( DWORD DWORD f fdwAccessdwAccess, , BOOL BOOL f fInheritInherit, , PCTSTR PCTSTR pszpszNameName ); ); 3、事件EVENT_ALL_ACCESS要求对事件对象进行完全访问EVENT_MODIFY_STATE允许SetEvent和ResetEvent函数SYNCHRONIZE允许事件对象的使用同步n一旦事件已经创建,就可以直接控制它的状态一旦事件已经创建,就可以直接控制它的状态n将事件设置为已通知状态将事件设置为已通知状态BOOL BOOL SetEvent(HANDLESetEvent(HANDLE hEventhEvent ); ); n将事件设置为未通知状态将事件设置为未通知状态BOOL BOOL ResetEvent(HANDLEResetEvent(HANDLE hEventhEvent ); );3、事件3、事件n事件的主要用途是标志事件的发生,并以事件的主要用途是标志事件的发生,并以此协调线程的执行顺序。此协调线程的执行顺序。n例例1:用户在主线程输入命令,控制新建线:用户在主线程输入命令,控制新建线程的运行。程的运行。#include#include#includeusingnamespacestd;CRITICAL_SECTIONcs;DWORDWINAPIThreadFunc(PVOIDparam)EnterCriticalSection(&cs);coutCreateThread:Createthreadisstartedendl;coutCreateThread:Createthreadiswaitingcontinuecommand.endl;LeaveCriticalSection(&cs);HANDLEphEvent=OpenEvent(EVENT_ALL_ACCESS,TRUE,ContinueCommand);WaitForSingleObject(phEvent,INFINITE);coutCreateThread:Recievedcontinuecommand.endl;coutCreateThread:Threadrunsagain.endl;Sleep(2000);coutCreateThread:Threadfinished.endl;return0;例例1intmain()InitializeCriticalSection(&cs);HANDLEhEvent=CreateEvent(NULL,FALSE,FALSE,ContinueCommand);coutMainThread:Creatingnewthread.endl;HANDLEThreadHandle=CreateThread(NULL,0,ThreadFunc,NULL,CREATE_SUSPENDED,NULL);coutMainThread:Newthreadcreated.endl;ResumeThread(ThreadHandle);stringinput;while(TRUE)EnterCriticalSection(&cs);coutMainThread:inputcommand,pleaseendl;LeaveCriticalSection(&cs);cout;cininput;if(input=continue)coutMainThread:Letthreadcontinuerunendl;SetEvent(hEvent);break;WaitForSingleObject(ThreadHandle,INFINITE);coutMainThread:Createthreadfinishedendl;DeleteCriticalSection(&cs);CloseHandle(hEvent);return0;例2n设计简单的文字管理软件,要求实现功能,设计简单的文字管理软件,要求实现功能,读文件、字数统计、拼写检查、语法检查读文件、字数统计、拼写检查、语法检查#includeHANDLEg_hEvent;voidOpenFileAndReadContentsIntoMemory()printf(OpenFileandReadcontentsintomemoryn);DWORDWINAPIWordCount(PVOIDpvParam)WaitForSingleObject(g_hEvent,INFINITE);printf(0:wordcountn);SetEvent(g_hEvent);/自自动return(0);DWORDWINAPISpellCheck(PVOIDpvParam)WaitForSingleObject(g_hEvent,INFINITE);printf(1:Spellcheckn);/Accessthememoryblock.SetEvent(g_hEvent);/自自动return(0);DWORDWINAPIGrammarCheck(PVOIDpvParam)WaitForSingleObject(g_hEvent,INFINITE);printf(2:Grammarcheckn);/Accessthememoryblock.SetEvent(g_hEvent);/自自动return(0);intmain()/g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);/人工重置人工重置g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);/自自动重置重置HANDLEhThread3;DWORDdwThreadID3;hThread0=CreateThread(NULL,0,WordCount,NULL,0,&dwThreadID0);hThread1=CreateThread(NULL,0,SpellCheck,NULL,0,&dwThreadID1);hThread2=CreateThread(NULL,0,GrammarCheck,NULL,0,&dwThreadID2);OpenFileAndReadContentsIntoMemory();/Allowall3threadstoaccessthememory.SetEvent(g_hEvent);WaitForMultipleObjects(3,hThread,TRUE,INFINITE);printf(mainthreadexitn);getchar();return0;例3:协调两个线程执行顺序n读操作、写操作读操作、写操作n先写后读先写后读HANDLEevRead,evFinish;DWORDWINAPIReadThread(PVOIDparam)WaitForSingleObject(evRead,INFINITE);coutReadingendl;SetEvent(evFinish);DWORDWINAPIWriteThread(PVOIDparam)coutWritingendl;SetEvent(evRead);intmain()evRead=CreateEvent(NULL,FALSE,FALSE,NULL);evFinish=CreateEvent(NULL,FALSE,FALSE,NULL);CreateThread(NULL,0,ReadThread,NULL,0,NULL);CreateThread(NULL,0,WriteThread,NULL,0,NULL);WaitForSingleObject(evFinish,INFINITE);coutTheProgramisEndendl;return0;4、互斥量n互斥量是一个种内核对象,确保线程拥有对互斥量是一个种内核对象,确保线程拥有对单个资源的互斥访问权。单个资源的互斥访问权。n一个使用数量一个使用数量n一个线程一个线程IDn一个递归计数器一个递归计数器n互斥量的线程互斥量的线程ID标识系统中哪个线程拥有互标识系统中哪个线程拥有互斥量,为斥量,为0,没有线程拥有,没有线程拥有n递归计数器指明线程拥有互斥量的次数递归计数器指明线程拥有互斥量的次数4、互斥量n经常用于保护多个线程访问的内存块;经常用于保护多个线程访问的内存块;n控制对共享资源的访问控制对共享资源的访问n保证每次只能有一个线程获得互斥量保证每次只能有一个线程获得互斥量4、互斥量n互斥量的创建,返回句柄互斥量的创建,返回句柄HANDLECreateMutex(PSECURITY_ATTRIBUTES psa,/安全属性的指针安全属性的指针BOOLbInitialOwner,/初始化互斥对象的所有者初始化互斥对象的所有者PCTSTRpszName /指向互斥对象名的指针指向互斥对象名的指针);InitialOwner:FALSE,互斥对象的线程ID和递归计数器均被设置为0。TRUE,互斥对象的线程ID被设置为调用线程的ID,递归计数器被设置为1。4、互斥量n为现有的一个已命名为现有的一个已命名互斥互斥对象创建一个新句柄对象创建一个新句柄 HANDLEOpenMutex(DWORDfdwAccess,/accessBOOLbInheritHandle,/inheritanceoptionPCTSTRpszName /objectname);MUTEX_ALL_ACCESS请求对互斥对象的完全访问MUTEX_MODIFY_STATE允许使用ReleaseMutex函数SYNCHRONIZE允许使用互斥对象同步n释放互斥量释放互斥量 HANDLE HANDLE ReleaseMutexReleaseMutex( ( HANDLE HANDLE hMutexhMutex); ); n等待互斥量等待互斥量DWORD DWORD WaitForSingleObjectWaitForSingleObject( ( HANDLE HANDLE hHandlehHandle, , DWORD DWORD dwMillisecondsdwMilliseconds ););n互斥量不同于其它内核对象,互斥对象有一个互斥量不同于其它内核对象,互斥对象有一个“线程所有线程所有权权”的概念。的概念。4、互斥量#include#includeusingnamespacestd;fstreamfile;DWORDWINAPIThreadFunc1(PVOIDparam)HANDLE*phMutex=(HANDLE*)param;for(inti=1;i=100;i+)WaitForSingleObject(*phMutex,INFINITE);fileThreadFunc1Outputiendl;ReleaseMutex(*phMutex); return0;例例1DWORDWINAPIThreadFunc2(PVOIDparam)HANDLE*phMutex=(HANDLE*)param;for(inti=1;i=100;i+)WaitForSingleObject(*phMutex,INFINITE);fileThreadFunc2Outputiendl;ReleaseMutex(*phMutex); return0;intmain()file.open(data.txt,ios:out);HANDLEhMutex=CreateMutex(NULL,FALSE,DisplayMutex);HANDLEThreadHandle1=CreateThread(NULL,0,ThreadFunc1,&hMutex,0,NULL);HANDLEThreadHandle2=CreateThread(NULL,0,ThreadFunc2,&hMutex,0,NULL);HANDLEhThread2=ThreadHandle1,ThreadHandle2;WaitForMultipleObjects(2,hThread,TRUE,INFINITE);CloseHandle(hMutex);file.close();return0;比较临界区、互斥量、事件n给数组元素赋值,并在屏幕打印出来给数组元素赋值,并在屏幕打印出来n改变起始值,无限重复上述过程改变起始值,无限重复上述过程#includewindows.hCRITICAL_SECTIONcs;inta5;DWORDWINAPIThread(PVOIDpParams)inti,num=0;while(TRUE)EnterCriticalSection(&cs);for(i=0;i5;i+)ai=num;LeaveCriticalSection(&cs);num+;return0;临界区临界区intmain()InitializeCriticalSection(&cs);CreateThread(NULL,0,Thread,NULL,0,NULL);while(TRUE)EnterCriticalSection(&cs);printf(%d%d%d%d%dn,a0,a1,a2,a3,a4);LeaveCriticalSection(&cs);return0;临界区临界区#includeHANDLEhMutex;inta5;DWORDWINAPIThread(PVOIDpParams)inti,num=0;while(TRUE)WaitForSingleObject(hMutex,INFINITE);for(i=0;i5;i+)ai=num;ReleaseMutex(hMutex);num+;return0;互斥量互斥量intmain()hMutex=CreateMutex(NULL,FALSE,NULL);CreateThread(NULL,0,Thread,NULL,0,NULL);while(TRUE)WaitForSingleObject(hMutex,INFINITE);printf(%d%d%d%d%dn,a0,a1,a2,a3,a4);ReleaseMutex(hMutex);return0;互斥量互斥量#includeHANDLEhEvent1,hEvent2;inta5;DWORDWINAPIThread(PVOIDpParams)inti,num=0;while(TRUE)WaitForSingleObject(hEvent2,INFINITE);for(i=0;i5;i+)ai=num;SetEvent(hEvent1);num+;return0;事件事件intmain()hEvent1=CreateEvent(NULL,FALSE,TRUE,NULL);hEvent2=CreateEvent(NULL,FALSE,FALSE,NULL);CreateThread(NULL,0,Thread,NULL,0,NULL);while(TRUE)WaitForSingleObject(hEvent1,INFINITE);printf(%d%d%d%d%dn,a0,a1,a2,a3,a4);SetEvent(hEvent2);return0;事件事件5、信号量n信号量是一个内核对象,可用来管理大量有限的系统信号量是一个内核对象,可用来管理大量有限的系统资源资源 n一个使用计数一个使用计数n3232位整数,最大资源数量位整数,最大资源数量n3232位整数,当前资源数量位整数,当前资源数量n信号量使用规则:信号量使用规则:n当前资源数量大于当前资源数量大于0 0,则等待信号量的线程获得资源继,则等待信号量的线程获得资源继续运行,当前资源数量减续运行,当前资源数量减1 1n当前资源数量等于当前资源数量等于0 0,则等待信号量的线程继续等待,则等待信号量的线程继续等待,直到有线程释放信号量,使当前资源数量大于直到有线程释放信号量,使当前资源数量大于0 0n创建信号量创建信号量5、信号量HANDLECreateSemaphore(PSECURITY_ATTRIBUTESpsa,LONGlInitialCount,/initialcountLONGlMaximumCount,/maximumcountPCTSTRpszName/objectname);n为现有的一个已命名信号机对象创建一个新句柄。为现有的一个已命名信号机对象创建一个新句柄。 5、信号量HANDLEOpenSemaphore(DWORD fdwAccess,BOOL bInheritHandle,/inheritanceoptionPCTSTR pszName /objectname);SEMAPHORE_ALL_ACCESS要求对信号量的完全访问;SEMAPHORE_MODIFY_STATE允许使用ReleaseSemaphore函数;SYNCHRONIZE允许使用信号量同步。n释放信号量释放信号量ReleaseSemaphore(HANDLEhSem,LONGlReleaseCount,PLONGplPreviousCount);5、信号量n等待互斥量等待互斥量DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);例例两个线程分别有一个初值为两个线程分别有一个初值为0的的int局部变局部变量,两个线程的行为是在一个循环中,使量,两个线程的行为是在一个循环中,使整型变量递增整型变量递增一个约束条件,在递增过程中,这两个值一个约束条件,在递增过程中,这两个值的差不超过的差不超过5HANDLEhsem1=CreateSemaphore(NULL,5,10,sem1);HANDLEhsem2=CreateSemaphore(NULL,5,10,sem2);inti1=0;inti2=0;DWORDWINAPIThreadFunc1(PVOIDparam)for(inti=1;i=100;i+)WaitForSingleObject(hsem1,INFINITE);ReleaseSemaphore(hsem2,1,NULL);i1+;filei1=i1i2=i2endl;return0;DWORDWINAPIThreadFunc2(PVOIDparam)for(inti=1;i=100;i+)WaitForSingleObject(hsem2,INFINITE);ReleaseSemaphore(hsem1,1,NULL);i2+;filei1=i1i2=i2endl;return0;生产者/消费者n有一个生产者进程,有两个消费者进程。有一个生产者进程,有两个消费者进程。生产者产生生产者产生1-100的的100个数。两个消费者个数。两个消费者从共享内存中取数。从共享内存中取数。#includeintarray5;/作作为仓库存放数据,最多可以放五个数据存放数据,最多可以放五个数据intpointer;/记录生成数据的个数生成数据的个数intpointerget;/记录取得的数据的位置取得的数据的位置intsum;/用来保存数据和用来保存数据和CRITICAL_SECTIONcsArray;/临界区界区对象象HANDLEhFull;/句柄,保存句柄,保存Full信号量信号量HANDLEhEmpty;/句柄,保存句柄,保存Empty信号量信号量/生生产者函数者函数DWORDWINAPIProducer(PVOIDpParam)inti=0;pointer=0;while(i100)WaitForSingleObject(hEmpty,INFINITE);EnterCriticalSection(&csArray);array(pointer+)%5=i+1;LeaveCriticalSection(&csArray);ReleaseSemaphore(hFull,1,NULL);i+;return0;/消消费者函数者函数ADWORDWINAPIConsumerA(LPVOIDlpParam)while(1)WaitForSingleObject(hFull,INFINITE);EnterCriticalSection(&csArray);sum+=array(pointerget+)%5;printf(ConsumerAget%dn,array(pointerget-1)%5);if(pointerget=100)printf(Thesumis%d,sum);LeaveCriticalSection(&csArray);ReleaseSemaphore(hEmpty,1,NULL);return0;/消消费者函数者函数BDWORDWINAPIConsumerB(LPVOIDlpParam)while(1)WaitForSingleObject(hFull,INFINITE);EnterCriticalSection(&csArray);sum+=array(pointerget+)%5;printf(ConsumerBget%dn,array(pointerget-1)%5);if(pointerget=100)printf(Thesumis%d,sum);LeaveCriticalSection(&csArray);ReleaseSemaphore(hEmpty,1,NULL);return0;voidmain()HANDLEhThreadProducer,hThreadConsumerA,hThreadComsumerB;sum=0;pointerget=0;InitializeCriticalSection(&csArray);hFull=CreateSemaphore(NULL,0,5,NULL);hEmpty=CreateSemaphore(NULL,5,5,NULL);hThreadProducer=CreateThread(NULL,0,Producer,NULL,0,NULL);hThreadConsumerA=CreateThread(NULL,0,ConsumerA,NULL,0,NULL);hThreadComsumerB=CreateThread(NULL,0, ConsumerB,NULL,0,NULL);getchar();哲学家吃饭问题n设有设有5个哲学家,共享一张放有个哲学家,共享一张放有5把椅子的桌子,每人分得把椅子的桌子,每人分得一把椅子,但是,桌子上共有一把椅子,但是,桌子上共有5只筷子,在每人两边各放只筷子,在每人两边各放一只,哲学家们在肚子饥饿时才试图分两次从两边拿起筷一只,哲学家们在肚子饥饿时才试图分两次从两边拿起筷子就餐。子就餐。n条件条件:n1)拿到两只筷子时哲学家才开始吃饭。)拿到两只筷子时哲学家才开始吃饭。n2)如果筷子已在他人手上,则该哲学家必须等他人吃完)如果筷子已在他人手上,则该哲学家必须等他人吃完之后才能拿到筷子。之后才能拿到筷子。n3)任一哲学家在自己未拿到两只筷子前却不放下自己手)任一哲学家在自己未拿到两只筷子前却不放下自己手中的筷子。中的筷子。#includeconstintPHILOSOPHERS=5;/哲学家人数哲学家人数constintTIME_EATING=50;/吃吃饭需要的需要的时间毫秒毫秒HANDLEeventPHILOSOPHERS;/主主线程同工作程同工作线程保持程保持同步的句柄数同步的句柄数组HANDLEmutexPHILOSOPHERS;/mutex数数组,这里相当于里相当于公共公共资源筷子源筷子CRITICAL_SECTIONcs;/控制打印的控制打印的临界区界区变量量DWORDWINAPIThreadFunc(PVOIDarg)intnum=*(int*)arg);DWORDret=0;while(1)ret=WaitForMultipleObjects(2,mutex,TRUE,1000);if(ret=WAIT_TIMEOUT)Sleep(100);continue;EnterCriticalSection(&cs);printf(philosopher%deattingn,num);LeaveCriticalSection(&cs);Sleep(TIME_EATING);break;SetEvent(eventnum);/设置置时间为有信号有信号return1;intmain()HANDLEhThread;InitializeCriticalSection(&cs);for(inti=0;iPHILOSOPHERS;i+)mutexi=CreateMutex(NULL,FALSE,NULL);eventi=CreateEvent(NULL,TRUE,FALSE,NULL);hThread=CreateThread(NULL,0,ThreadFunc,(void*)&i,0,NULL);if(hThread=0)printf(createthread%dfailedwithcode:%dn,i,GetLastError();DeleteCriticalSection(&cs);return-1;CloseHandle(hThread);/等待所有的哲学家吃等待所有的哲学家吃饭结束束DWORDret=WaitForMultipleObjects(PHILOSOPHERS,event,TRUE,INFINITE);if(ret=WAIT_OBJECT_0)printf(allthephilosophershadadinner!n);else printf(WaitForMultipleObjectsfailedwithcode:%dn,GetLastError();DeleteCriticalSection(&cs);for(intj=0;jPHILOSOPHERS;j+)CloseHandle(mutexj);getchar();return1;n线程的优先级决定它何时运行和接收多少线程的优先级决定它何时运行和接收多少CPU时时间。间。n优先级共优先级共32级,是从级,是从0到到31的数值,称为基本优的数值,称为基本优先级别。先级别。n0-15级是普通优先级级是普通优先级n线程的优先级可以动态变化线程的优先级可以动态变化n高优先级线程优先运行,只有高优先级线程不运行时,高优先级线程优先运行,只有高优先级线程不运行时,才调度低优先级线程运行。才调度低优先级线程运行。n优先级相同的线程按照时间片轮流运行。优先级相同的线程按照时间片轮流运行。进程与线程的优先级进程与线程的优先级n16-31级是实时优先级级是实时优先级n相同优先级线程的运行不按照时间片轮转,而是先运相同优先级线程的运行不按照时间片轮转,而是先运行的线程就先控制行的线程就先控制CPU,如果它不主动放弃控制,同,如果它不主动放弃控制,同级或低优先级的线程就无法运行。级或低优先级的线程就无法运行。进程与线程的优先级n线程的实际优先级设置是两个值的结合:线程的实际优先级设置是两个值的结合:线程优先级线程优先级=进程优先级类进程优先级类+线程相对优先级线程相对优先级进程优先级进程优先级进程优先级进程优先级标志标志优先级值优先级值idleIDLE_PRIORITY_CLASS4BelowBELOW_NORMAL_PRIORITY_CLASS此值在此值在2000以以下系统不支持下系统不支持normalNORMAL_PRIORITY_CLASS前台为前台为9,后台后台为为7AboveABOVE_NORMAL_PRIORITY_CLASS此值在此值在2000以以下系统不支持下系统不支持highHIGH_PRIORITY_CLASS13RealtimeREALTIME_PRIORITY_CLASS24线程优先级线程优先级线程优先级线程优先级标志标志优先级值优先级值idleTHREAD_PRIORITY_IDLE如果进程优先级为如果进程优先级为realtime则调整为则调整为16,其它情况为,其它情况为1LOWESTTHREAD_PRIORITY_LOWEST-2BelowTHREAD_PRIORITY_BELOW_NORMAL-1normalTHREAD_PRIORITY_NORMAL不变不变AboveTHREAD_PRIORITY_ABOVE_NORMAL+1highTHREAD_PRIORITY_HIGHEST+2CRITICALTHREAD_PRIORITY_TIME_CRITICAL如果进程优先级为如果进程优先级为realtime则调整为则调整为31,其它情况为,其它情况为15关于优先级的操作n在程序中,可以获取和更改进程和线程的优先级在程序中,可以获取和更改进程和线程的优先级n对于进程的优先级类有如下函数:对于进程的优先级类有如下函数:DWORDGetPriorityClass(HANDLEhProcess);BOOLSetPriorityClass(HANELhProcess,DWORDdwPriority);n对线程的优先级有这两个函数:对线程的优先级有这两个函数:intGetThreadPriority(HANDLEhThread);BOOLSetThreadPriority(HANDLEhThread,intnPriority);关于优先级的操作nSetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_IDLE);/设置为最低设置为最低nSetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_LOWEST);nSetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_BELOW_NORMAL);nSetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL);
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号