版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
统,最优秀的序员也只能通过驻留内存的方式实现所谓的"多任务",而如今的Win32操作系统却可以一理解多线及其同步、互斥等通信方式是理解现代操作系统的关键一环,当我们精通了Win32多线因此,学习Win32多线不仅对理解Win32本身有重要意义,而且对学习和其它操作系统也有触类进与进(roce)是具有一定独立功能的序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实因待资源或而被处于等待状态,因完成任务而被撤消,反映了一个序在一定的数据集上运行的全部动态过。线和进的关系是:线是属于进的,线运行在进空间内,同一进所产生的线共享同一内存空间,当进退出时该进所产生的线都会被强制退出并清除。线可与属于同一进的其它线单进、单线,MS-DOS大致是这种操作系统多进、单线,多数UNIX(及类UNIX的LINUX)是这种操作系统多进、多线,Win32(WindowsNT/2000/XP等)、Solaris2x和OS/2都是这种操作系统单进、多线,VxWorks是这种操作系统在进内创建、终止线比创建、终止进要快模型中,子进必须父进地址空间的问题。1、进间通信剪贴板(Clip动态数据交换(DynamicData部件对象模型(ComponentObject文件映射(FileMap邮件槽(Mail管道Win32套接字过调用(RemoteProcedureWM_COPYDATA消息(WM_COPYDATAMessage)在WIN32中,可使用在PSAPI.DLL中提供的ProcessstatusHelper函数帮助我们获取进信息EnumProcesses()函数可以获取进的ID,其原型为参数lpidProcess:一个足够大的DWORD类型的数组,用于存放进的ID值;参数cb:存放进ID值的数组的最大长度,是一个DWORD类型的数据;参数cbNeeded:指向一个DWORD类型数据的指针,用于返回进的数目;组中,进个数存放在cbNeeded参数所指向的变量中;如果调用失败,返回FALSE。GetModuleFileNameExA()函数可以实现通过进句柄获取进文件名,其原型为DWORDDWORDGetModuleFileNameExA(HANDLEhProcess,HMODULElpstrFileName,DWORD参数hProcess:接受进句柄的参数,是HANDLE类型的变量;参数hModule:指针型参数,在本文的序中取值为NULL;参数lpstrFileName:LPTSTR类型的指针,用于接受主调函数传递来的用于存放进名的字符数组参数nsize:lpstrFileName名存放在lpstrFileName0////获取当前进总数EnumProcesses(process_ids,sizeof(process_ids),for(inti=0;i<num_processes;{//根据进IDprocess[i]=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,0,//if(GetModuleFileNameExA(process[i],NULL,File_name,cout<<fileName<<}Win32WIN32靠线的优先级(达到抢占式多任务的目的)及分配给线的CPU时间来调度线。WIN32保存当前执行线的描述表(上下文装入下一执行线的描述表(上下文优先级0为系统保留。它们可以分成两类,即实时优先级和可变优先级:Win32对有者,甚至可以跨进。有一组WIN32API与对象关:WaitForSingleObject,用于等待对象的"激活",其函数原型WaitForSingleObject(HANDLEhHandle,//等待对象的句柄DWORDdwMilliseconds等待毫秒数,INFINITEMemoryresourcenotification、Mutex、Process、Semaphore、Thread和Waitabletimer。如果等待的对象不可用,那么线就会挂起,直到对象可用线才会被唤醒。对不同的对象,WaitForSingleObject表现为不同的含义。例如,使用WaitForSingleObject(hThread,…)可以判断一个线是否结束;使用WaitForSingleObject(hMutex,…)可以判断是否能够进入临界区;而WaitForSingleObject(hProcess,…)则表现为等待一个进的结束。bWaitAll,DWORDdwMilliseconds);CloseHandle在VC++6.0中,有两种多线编方法:一是使用C运行时库及WIN32API函数,另法是使用MFC,MFC对多线开发有强大的支持。VisualC++提供了两种版本的C运行时库,-个版本供单线应用序调用,另一个版本供多线应用序类似errno的全局变量,每个线单独设置一个;VisualC++提供的多线运行时库又分为静态库和动态库两类,而每一类运行时库又可再分为debug版和release版,因此VisualC++共提供了6个运行时库。如下表:Singlethread(staticDebugsinglethread(staticMultiThread(staticDebugmultiThread(staticMultiThread(dynamic使用标准C库(基于单线)并且只允重入函数集进行库调用使用Win32API线管理函数,如Win32服务(EnterCriticalSectionLeaveCriticalSection函数),为不如果使用标准C库而调用VC运行时库函数,则在序的link阶段会提示如下错误errorLNK2001:errorLNK2001:unresolvedexternalsymbolendthreadexerrorLNK2001:unresolvedexternalsymbolWIN32线控制主要实现线的创建、终止、挂起和恢复等操作,这些操作都依赖于WIN32提供的一组API和具体编译器的C运行时库函数。线函作为参数,返回一个UINT,线函数的结构为UINTUINTThreadFunction(LPVOID{ }{//Do}上this指针。请看下列序:#include#include"windowsh"#include<process.h>classExampleTask{voidStartTask();void{}intmain(intargc,char*{return0;}errorerrorC2664:'_beginthread':cannotconvertparameter1from'void(void*)'to'void(cdeclNoneofthefunctionswiththisnameinscopematchthetarget#include#include"windowsh"#include<process.h>classExampleTask{intmain(intargc,char*{ExampleTaskreturn0;}errorerrorC2664:'_beginthread':cannotconvertparameter1from'void(void*)'to'void(cdeclNoneofthefunctionswiththisnameinscopematchthetarget将该成员函数为static类型,去掉this指针;#include#include"windowsh"#include<process.h>classExampleTask{voidStartTask();void{}intmain(intargc,char*{return0;}#includeprocess.h>class{intintmain(intargc,char*{return0;}函数只能static成员。解决此问题的一种途径是可以在调用类静态成员函数(线函数)时将this指针作为参数传入,并在改线函数中用强制类型转换将this转换成指向该类的指针,通过该指针非静态#include#include"windowsh"#include<process.h>classExampleTask{voidStartTask();{ExampleTask*pTaskMain=(ExampleTask*)//通过pTaskMain}void{}intmain(intargc,char*{return0;}进的主线操作系统自动生成,Win32提供了CreateThreadAPI来完成用户线的创建,该HANDLEHANDLELPSECURITY_ATTRIBUTESlpThreadAttributes,//PointertoaSECURITY_ATTRIBUTESSIZE_TdwStackSize,//Initialsizeofthestack,inLPVOIDlpParameter,//PointertoavariabletobepassedtothethreadDWORDdwCreationFlags,//FlagsthatcontrolthecreationofthethreadLPDWORDlpThreadId//Pointertoavariablethatreceivesthethreadidentifier用C/C++运行时库中的_beginthread(或_beginthreadex),其函数原型为:uintptr_tuintptr_tvoid(cdecl*start_address)(void*),//Startaddressofroutinethatbeginsexecutionofunsignedstack_size,//Stacksizefornewthreadorvoid*arglist//Argumentlisttobepassedtonewthreadoruintptr_tvoid*security,//PointertoaSECURITY_ATTRIBUTESstructureunsignedstack_size,unsigned(stdcall*start_address)(void*),void*arglist,unsignedinitflag,//Initialstateofnewthread(0forrunningorCREATE_SUSPENDEDforunsigned_beginthreadWin32API中的CreateThread通过_beginthread函数我们可以利用其参数列表arglist将多个参数传递到线_beginthread函数初始化某些C运行时库变量,若需要使用C运行时库线自身调用ExitThread函数即终止自己,其原型为VOIDVOIDExitThread(UINTfuExitCode它将参数fuExitCode设置为线的退出码C/C++C/C++运行时库函数_endthread_endthreadex)终止线_endthread函数对于线内的条件终止很有用。例如,专门用于通信处理的线若无法获取对通信端口的同一进或其他进的线调用TerminateThread函数,其原型为该函数用来结束由hThread参数指定的线,并把dwExitCode设成该线的退出码。当某个线不再包含线的进终止当我们创建线的时候,如果给其传入CREATE_SUSPENDED标志,则该线创建后被挂起,我们应使用ResumeThread恢复它:如果ResumeThread函数运行成功,它将返回线的前一个暂停计数,否则返回0xFFFFFFFF。对于没有被挂起的线,序员可以调用SuspendThread函数强行挂起之:一个线可以被挂起多次。线可以自行暂停运行,但是不能自行恢复运行。如果一个线被挂起次,则该线也必须被恢复n次才可能得以执行 函数改变线的相对优先级。一个线的优先级是相对于其所属进的优先级而言的BOOLBOOLSetThreadPriority(HANDLEhThread,int其中参数hThread是指向待修改优先级线的句柄,线与包含它的进的优先级关系如下:线优先级=进类基本优先级+线相对优先级高:HIGH低于正常:BELOW_NORMAL空闲:IDLE_PRIORITY_CLASS最低线低于正常线正常线:THREAD_PRIORITY_NORMAL(缺省高于正常线最高线HANDLEHANDLEhCurrentThread=//获得该线句柄SetThreadPriority(hCurrentThread,该函数可使线暂停自己的运行,直到dwMilliseconds毫秒过去为止。它告诉系统,自身不想在某个IntIntGetThreadPriority(HANDLEBOOLBOOLGetExitCodeThread(HANDLEhThread,我们可通过GetLastError()获知错误原因。如果线尚未结束,lpExitCode带回来的将是STILL_ALIVE。获得获得/设置线上下文BOOLGetThreadContext(BOOLSetThreadContext(HANDLE由于GetThreadContext和SetThreadContext可以操作CPU内部的寄存器,因此在一些高级技巧的编中有一定应用。譬如,调试器可利用GetThreadContext挂起被调试线获取其上下文,并设置上下文中的标志寄存器中的陷阱标志位,最后通过SetThreadContext使设置生效来进行单步调试。以下序使用CreateThread创建两个线,在这两个线Sleep一段时间,主线通GetExitCodeThread来判断两个线是否结束运行#include<stdioh>#include<stdlibh>#include<conioh>DWORDWINAPIint{HANDLEhThrd1;HANDLEhThrd2;DWORDthreadId;hThrd1=CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&threadId);if(hThrd1)printf("Thread1hThrd2=CreateThread(NULL,0,ThreadFunc,(LPVOID)2,0,&threadId);if(hThrd2)printf("Thread2//KeepwaitinguntilbothcallstoGetExitCodeThreadsucceed//neitherofthemreturnsSTILL_ACTIVE.for(;;){printf("Pressanykeytoif(exitCode1==STILL_ACTIVE)puts("Thread1isstillrunning!");if(exitCode2==STILL_ACTIVEputs("Threadputs("Thread2isstillif(exitCode1!=STILL_ACTIVE&&exitCode2!=STILL_ACTIVE}printf("Thread1returned%d\n",exitCode1);printf("Thread2returned%d\n",return}*Takethestartupvalue,dosomesimplemathon*andreturnthecalculated{return(DWORD)n*10;}#include<stdioh>#include<stdlibh>DWORDWINAPIint{HANDLEhThrd;inti;for(i=0;i<5;{hThrd=CreateThread(NULL,0,ThreadFunc,(LPVOID)i,0,&threadId);if(hThrd){{printf("Threadlaunched%d\n",}}//Waitforthethreadstoreturn}{intfor(i=0;i<10;i++)printf("%d%d%d%d%d%d%d%d\n",n,n,n,n,n,n,n,n);return}运行的输出具有很大的随机性,这里了几次结果的一部分(几乎每一次都不同如果我们使用标准C库函数而不是多线版的运行时库,则序可能输出" 下列序在主线创建一个SecondThread,在SecondThread线通过自增对Counter计数//WaituntilsecondthreadterminatesWaitForSingleObject(hThread,INFINITE);//WaituntilsecondthreadterminatesWaitForSingleObject(hThread,INFINITE);printf("Countershouldbe }//CreatethesecondhThread=(HANDLE)_beginthreadex(NULL,0,&SecondThreadFunc,NULL,0,printf("Creatingsecondint{unsignedreturn0;})while(Counter stdcallSecondThreadFunc(void{printf("Insecond#include<stdioh>线互斥是指对于共享的操作系统资源(指的是广义的"资源",而不是Windows的.res文件,譬如(1)信号量互斥量临界区(Criticalsection)intintvar;UINT{var=while(var<{// ::InterlockedIncrement(long*)}return}请看下列序intglobalFlag={return}int{HANDLEhThrd;DWORDhThrd=CreateThread(NULL,0,ThreadFunc,NULL,0,ifif{}while;}上述序中使用全局变量和while循环查询进行线间同步,实际上,这是一种应该避免的方法,当主线必须使自己与ThreadFunc函数的完成运行实现同步时,它并没有使自己进入睡眠状态由于主线没有进入睡眠状态,因此操作系统继续为它调度CPU时间,这就要占用其他线的宝贵时间的情况。因为在这种情况下,系统决不会将任何时间片分配给ThreadFunc线。发状态(unsignalorfalse)。根据状态变迁方式的不同,可分为两类:手动设置:这种对象只可能用序手动设置,在需要该或者发生时,采用SetEventResetEvent来进行设CreateEvent(LPSECURITY_ATTRIBUTESSECURITY_ATTRIBUTES结构指针,可为NULLBOOLbManualReset,//手动///TRUE:在WaitForSingleObject后必须手动调用ResetEvent//FALSE:在WaitForSingleObject后,系统自动清 信BOOLbInitialStateLPCTSTRlpName 使用""机制应注意以下事项由于event对象属于内核对象,故进B可以调用OpenEvent函数通过对象的名字获得进A中照变量名来该结构体。VOIDVOIDInitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection//指向序员定义的CRITICAL_SECTION该函数用于对pcs所指的CRITICAL_SECTION结构体进行初始化。该函数只是设置了一些成员变量,VOIDVOIDDeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection//指向一个不再需要的CRITICAL_SECTION变VOIDVOIDEnterCriticalSection(ION//指向一个你即将锁定的CRITICAL_SECTIONVOIDVOIDLeaveCriticalSection(////指向一个你即将离开的CRITICAL_SECTIONvoidvoid{...//dosomething}每个共享资源使用一个CRITICAL_SECTION如果需要同时多个资源,则可能连续调用CriticalSection不是OS对象,如果进入临界区的线"挂"了,将无法释放临界资源。这个缺点在Mutex中得到了弥补。互斥量的作用是保证每次只能有一个线获得互斥量而得以继续执行,使用CreateMutex函数创建CreateMutex(LPSECURITY_ATTRIBUTES//安全属性结构指针,可为NULLBOOLbInitialOwner,LPCTSTRlpNameMutex是对象,可以跨进,下面的代码给出了从另一进命名Mutex的例子HANDLEHANDLEif…}…}ReleaseMutex(HANDLEvoidvoid{...//dosomething}互斥(te)内核对象能够确保线拥有对单个资源的互斥权。互斥对象的行为特性与临界区相texCritlSction的如下不同:不同进的多个线能够单个互斥对象信号量是0到指定最大值之间的同步对象。信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的。信号量对象在控制上可以支持有限数量共享资源的。HANDLECreateSemaphoreHANDLECreateSemaphore(LONGl umCount,//最大资源数通过调用ReleaseSemaphore函数,线就能够对信标的当前资源数量进行递增,该函数原型为BOOLBOOLReleaseSemaphore(LONGlReleaseCount,信号量的当前资源数增加lReleaseCountLPLONGlpPreviousCountHANDLEHANDLEOpenSemaphore(DWORDfdwAccess,BOOLbInherithandle,PCTSTRintintglobalVar={returnreturn}{return0;}movmoveax,[globalVarDaddmov[globalVar在"moveax,[globalVar(0042d3f0)]"指令与"addeax,1"指令以及"addeax,1"指令与"mov[globalVar用InterlockedExchangeAdd函数解决这个问题:intintglobalVar={return0;}{return0;}InterlockedExchangeAdd保证对变量globalVar的具有"原子性"。互锁的控制速度非常快,调CPU50,不需要进行用户方式与内核方式的切换(该切换通常需要运行1000个CPU周期)。CreateWaitableTimer(BOOLfManualReset,//可等待定时器对象在非激活状态下被创建,序员应调用SetWaitableTimer函数来界定定时器在何时BOOLBOOL//LONGlPeriod,//指明此后定时器应该间隔多长时间激活一次 BOOLfResume);BOOlBOOlWaitableTimer(HANDLEhTimer(DWORDfdwAccess,BOOLbInherithandle,PCTSTR#include<stdioh>{longiThreadID;while{printf("\n线 printf("\n线 printf("\n线1释放两个临界区");return}longWINAPIThreadFn(long{while{printf("\n线 printf("\n线 printf("\n线2释放两个临界区}线1占用临界区1或或或如果线2的控制函数改为longlongWINAPIThreadFn(long{while{printf("\n线21");printf("\n线22");printf("\n线2释放两个临界区");}消除了线1、2相互等待资源的可能性。*AfxBeginThread(,//intnPriorityTHREAD_PRIORITY_NORMAL线的优先级UINTnStackSize0,线DWORDdwCreateFlags0,线LPSECURITY_ATTRIBUTESlpSecurityAttrsNULL线的安全////线控制函数{if(lpObjectNULL||!lpObject->IsKindof(RUNTIME_CLASS(CExampleClass)))return-1;//输入参数//线成功启动while{}return}//在 UI创建用户界面线时,必须首先从CWinThread派生类,并使用DECLARE_DYNCREATEIMPLEMENT_DYNCREATE宏此类下面给出了CWinThread类的原型(添加了关于其重要函数功能和是否需要被继承类重载的注释classclassCWinThread:public{//ConstructorsBOOLCreateThread(DWORDdwCreateFlags=0,UINTnStackSize=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);//CWnd*CWnd*m_pMainWnd;//mainwindow(usuallysameAfxGetApp()->m_pMainWnd)CWnd*m_pActiveWnd;//activemainwindow(maynotbem_pMainWnd)BOOLm_bAutoDelete;//enables'deletethis'afterthread//onlyvalidwhilerunningHANDLEm_hThread;//thisthread'sHANDLEoperatorHANDLE()DWORDm_nThreadID;//thisthread'sintBOOLSetThreadPriority(int//OperationsDWORDDWORD ram,LPARAM////virtualBOOL//runningandidle//virtualint//消息调度到TranslateMessage和 essage之前对其进行筛选virtualBOOLPumpMessage();//lowlevelmessagevirtualBOOLOnIdle(LONGlCount);//returnTRUEifmoreidleprocessingvirtualBOOLIsIdleMessage(MSG*pMsg);//checksforspecialmessages//virtualintExitInstance();//defaultwill'delete//截获由线的消息和命令处理 virtualLRESULTProcessWndProcException(CException*e,constMSG*//Advanced:handlingmessagessenttomessagefiltervirtualBOOLProcessMessageFilter(intcode,LPMSG////Advanced:virtualaccesstom_pMainWndvirtualCWnd*GetMainWnd();#ifdef_DEBUGvirtualvoidAssertValid()virtualvoidDump(CDumpContext&dc)intm_nDisablePumpCount;//Diagnostictraptodetectillegalre-entrancyvirtualvoidDelete();//'deletethis'onlyifm_bAutoDelete==//messagepumpforRunMSGm_msgCur;//currentmessage//constructorusedbyimplementationofCWinThread(AFX_THREADPROCpfnThreadProc,LPVOID//validafterLPVOIDm_pThreadParams;//genericparameterspassedtostartingfunctionAFX_THREADPROCm_pfnThreadProc;//setafterOLEisvoid(AFXAPI*m_lpfnOleTermOr COleMessageFilter*m_pMessageFilter;CPointm_ptCursorLast;//lastmousepositionUINTm_nMsgLast;//lastmousemessageBOOLDispatchThreadMessageEx(MSG*msg);//helpervoidDispatchThreadMessage(MSG*msg);//obsolete启动UI线的AfxBeginThread函数的原型为CWinThreadCWinThread//从CWinThreadRUNTIME_CLASSCRuntimeClass*pThreadClass,UINTnStackSize=0,DWORDdwCreateFlags=0,DWORDdwCreateFlags=0,我们可以方便地使用VC++6.0类向导定义一个继承自CWinThread的用户线类。下面给出产生我们自定义的CWinThread子类CMyUIThread的方法。打开VC++6.0BaseClass类为CWinThreadCMyUIThread,点击"OK"按钮后就产生了类CMyUIThread。//CMyUIThreadclassCMyUIThread:public{CMyUIThread();//protectedconstructorusedbydynamic//////ClassWizardgeneratedvirtualfunctionvirtualintExitInstance();virtual//Generatedmessagemap//NOTE-theClassWizardwilladdandremovememberfunctions//IMPLEMENT_DYNCREATE(CMyUIThread,BOOL{//TODO:performandper-threadinitializationherereturnTRUE;}int{//TODO:performanyper-threadcleanupherereturnCWinThread::ExitInstance();}BEGIN_MESSAGE_MAP(CMyUIThread,//NOTE-theClassWizardwilladdandremove macrosCMyUIThread*pThread;CMyUIThread*pThread;另外,我们也可以不用AfxBeginThread创建线,而是分如下两步完成调用CWinThread::CreateThread函数来启动该线自身内调用AfxEndThread函数可以终止该线voidvoidUINTnExitCode//theexitcodeofthe候捷先生早些时间喜欢为MDI序的每个窗口创建一个线,他后来澄清了这个错误。因为如果线间通MFCCSyncObjectCCriticalSection、CCEvent、CMutex、CSemaphore类封装和CCriticalSection、CCEvent、CMutex、CSemaphore类,下面是CSyncObjectclassclassCSyncObject:public{//HANDLEm_hObject;//virtualBOOLLock(DWORDdwTimeout=INFINITE);virtualBOOLUnlock()=0;virtualBOOLUnlock(LONG/*lCount*/,LPLONG/*lpPrevCount=NULL{returnTRUE;#ifdef_DEBUGCStringm_strName;virtualvoidAssertValid()virtualvoidDump(CDumpContext&dc)const;friendclassCSyncObject类最主要的两个函数是Lock和Unlock,若我们直接使用CSyncObject类及其派生类,我们需要非常地在Lock之后调用Unlock。序提供了更灵活的机制,下面以实际来阐述CSingleLock的用法:classclass{{m_pCWnd=}voidvoidPaintBall(COLORREFcolor,CRect&rc);CWndvoidCThreadSafeWnd::PaintBall(COLORREFcolor,CRect{if(csl.Lock());{//notCDC*pdc=m_pCWnd->GetDC();CBrushCBrush*oldbrush=pdc-GdiFlush();//don'twaittoupdatethedis }}上述实例讲述了用CSingleLockWindowsGDI相关对象进行保护的方法,下面再给出一个其他方面intarray1[10],array2[10];CMutexSectionintarray1[10],array2[10];CMutexSectionsection;创建一个CMutex类的对象//赋值线控制函数{for(inti=0;i<10;array1[i]=i;}//拷贝线控制函数{for(inti=0;i<10;i++)array2[i]=array1[i];}}AfxBeginThread(CopyThread,NULL);//启动拷贝线上面的例子中启动了两个线EvaluateThread和CopyThread,线EvaluateThread把10个数赋值给数组array1[],线CopyThread将数组array1[]拷贝给数组array2[]。由于数组的拷贝和赋值都是整体行为,forfor(inti=0;i<10;i++)array1[i]=i;和forfor(inti=0;i<10;array2[i]=可以利用PostThreadMessage函数间发送消息:BOOLBOOLPostThreadMessage(DWORDidThread,//threadidentifierUINTMsg,//messagetopost ram,//firstmessageLPARAMlParam//secondmessage"定理""定理" 屏幕上的每一个控件都是一个窗口,有对应的窗口函数消息的发送通常有两种方式,一是SendMessage,一是PostMessage,其原型分别LRESULTLRESULTSendMessage(HWNDhWnd,//handleofdestinationUINTMsg,//messageto ram,//firstmessageparameterLPARAMlParam//secondmessageBOOLPostMessage(HWNDhWnd,//handleofdestinationUINTMsg,//messageto ram,//firstmessageparameterLPARAMlParam//secondmessage两个函数原型中的四个参数的意义相但是SendMessage和PostMessage的行为有差SendMessage必另一个线,则会发生线上下文切换,等待另一线理完成消息。为了防止另一线掉,导致SendMessage不能返回,我们可以调用SendMessageTimeout函数:SendMessageTimeout(HWNDhWnd,//handleofdestinationwindowUINTMsg,//messageto ram,//firstmessageparameterLPARAMlParam,//secondmessageUINTfuFlags,//howtosendthemessageUINTuTimeout,//time-outdurationLPDWORDlpdwResult//returnvalueforsynchronous系,为此我们需要简单地叙述一下MFC序的"生死因果"(:《深入浅出MFC》)。使用VC++6.0的向导完成一个最简单的单文档架构MFC应用序输入MFCEXE工名选择单文档架构,不支 /View结构MFCThreadhclassclassCMFCThreadApp:public{////ClassWizardgeneratedvirtualfunctionvirtualBOOL//afx_msgvoid//NOTE-theClassWizardwilladdandremovememberfunctions//DONOTEDITwhatyouseeintheseblocksofgeneratedcodeMFCThread.cpp文//CMFCThreadAppBOOL{…CMainFrame*pFrame=newCMainFrame;m_pMainWnd=pFrame;//createandloadtheframewithits//Theoneandonlywindowhasbeeninitialized,soshowandupdateit.return}}#include#include"ChildViewclassCMainFrame:public{////ClassWizardgeneratedvirtualfunctionvirtualBOOLOnCmdMsg(UINTnID,intnCode,void*pExtra,#ifdef_DEBUGvirtualvoidAssertValid()virtualvoidDump(CDumpContext&dc)const;CChildView//NOTE-theClassWizardwilladdandremovememberfunctions//DONOTEDITwhatyouseeintheseblocksofgenerated//NOTE-theClassWizardwilladdandremove macros//DONOTEDITwhatyouseeintheseblocksofgeneratedcode//CMainFrame{//TODO:addmemberinitializationcode}BOOLCMainFrame::PreCreateWindow(CREATESTRUCT&{if(!CFrameWnd::PreCreateWindow(cs)return//TODO:ModifytheWindowclassorstyleshereby//theCREATESTRUCTcs.dwExStyle&=cs.lpszClass=AfxRegisterWndClass(0);returnTRUE;}ChildViewh////CChildViewclassCChildView:public{////ClassWizardgeneratedvirtualfunctionvirtualBOOLPreCreateWindow(CREATESTRUCT&virtual//Generatedmessagemapfunctionsafx_msgvoidChildView.cpp//BEGIN_MESSAGE_MAP(CChildView,CWnd//CChildViewmessageBOOLCChildView::PreCreateWindow(CREATESTRUCT&{{ifreturncs.dwExStyle|=cs.style&=~WS_BORDER;cs.lpszClass=return}void{CPaintDCdc(this);//devicecontextfor//TODO:Addyourmessagehandlercode//DonotcallCWnd::OnPaint()forpainting}文件MFCThreadh和MFCThread.cpp定义的类CMFCThreadApp继承自CWinApp类,而CWinApp类又继承自CWinThread类(CWinThread类又继承自CCmdTarget类),CMFCThread本质上是一个MFC线类,下图给出了相关的类层次结构:classclassCWinApp:public{//CWinApp(LPCTSTRlpszAppName=NULL);//defaultapp////Startupargs(donotchange)HINSTANCEm_hInstance;LPTSTRm_lpCmdLine;intint//Runningargs(canbechangedinLPCTSTRm_pszAppName;//humanreadablenameLPCTSTRm_pszExeName;//executablename(nospaces)LPCTSTRm_pszHelpFilePath;//defaultbasedonmodulepathLPCTSTRm_pszProfileName;//defaultbasedonappname//virtualBOOLInitInstance();virtualintExitInstance();//returnappexitcodevirtualintRun();virtualBOOLOnIdle(LONGlCount);//returnTRUEifmoreidleprocessingvirtualLRESULTProcessWndProcException(CException*e,constMSG*pMsg);SDK序的WinMain所完成的工作现在由CWinApp的三个函数完成virtualBOOLInitInstance();virtualintRun();"CMFCThreadApptheApp;"语句定义的全局变量theApp是整个式的applicationobject,每一个MFC应用序都有一个。当我们执行MFCThread序的时候,这个全局变量被构造。theApp配置完成后,WinMain开始执行。但是序中并没有WinMain的代码,它在哪里呢?原来MFC早已准备好并由Linker直接加到应用序代码中的,其原型为(存在于VC++6.0安 下提供的APPMODUL.CPP文件中externextern"C"intLPTSTRlpCmdLine,intnCmdShow){//callshared/exported}其中调用的AfxWinMain如下(存在于VC++6.0安 LPTSTRLPTSTRlpCmdLine,int{ASSERT(hPrevInstance==intnReturnCode=-1;CWinApp*pApp=//AFXinternalif(!AfxWinInit(hInstance,hPrevInstance,lpCmdLine,goto//Appglobalinitializationsif(pApp!=NULL&&!pApp-gotoif(!pThread->InitInstance()){if(pThread->m_pMainWnd!={}gotoInitFailure;}nReturnCode=pThread-#ifdef//CheckformissingAfxLockTempMapif(AfxGetModuleThreadState()->m_nTempMapLock!={TRACE1("Warning:Tempmaplockcountnon-zero(%ld).\n",}}CWinApp*pApp=AfxGetApp();其中,InitApplication是窗口类别的场所;InitInstance是产生窗口并显示窗口的场所;Run是提取并分派消息的场所。这样,MFC就同WIN32SDK序对应起来了。CWinThread::Run是序生命的"活水"(:《深入浅出MFC》,函数存在于VC++6.0安装下提供的THRDCORE.CPP文件中):////mainrunningroutineuntilthreadexitsintCWinThread::Run(){//fortrackingtheidletimeBOOLbIdle=LONGlIdleCount=//acquireanddispatchmessagesuntilaWM_QUITmessageisreceived.for(;;){//phase1:checktoseeifwecandoidle{//callOnIdlewhileinbIdlestateif(!OnIdle(lIdleCount++))bIdle=FALSE;//assume"noidle"}//phase2:pumpmessageswhileavailable{//pumpmessage,butquitonWM_QUITif(!PumpMessage())return//reset"noidle"stateafterpum if(IsIdleMessage(&m_msgCur)){bIdlebIdle=TRUE;lIdleCount=}}while(::PeekMessage(&m_msgCur,NULL,NULL,NULL,}ASSERT(FALSE);//not}//CWinThreadimplementationBOOL{if(!::GetMessage(&m_msgCur,NULL,NULL,{return}//processthisif(m_msgCurmessage!=WM_KICKIDLE&&{ }return}因此,忽略IDLE状态,整个RUNdodo }while消息的"绕行"过在MFC中,只要是CWnd衍生类别,就可以拦下任何Windows消息。与窗口无关的MFC类别(例如 和CWinApp)如果也想处理消息,必须衍生自CCmdTarget,并且只可能收MANDMESSAGE_MAP的类都继承自CCmdTargetMFC中MESSAGE_MAP的定义依赖于以下三个宏theClass,//SpecifiesthenameoftheclasswhosemessagemapthisisbaseClass//SpecifiesthenameofthebaseclassoftheClass我们序中涉及到的有:MFCThread.h、MainFrmh、ChildView.h文//NOTE-theClassWizardwilladdandremove macros//DONOTEDITwhatyouseeintheseblocksofgenerated//NOTE-theClassWizardwilladdandremove macros//DONOTEDITwhatyouseeintheseblocksofgeneratedcode)由这些宏,MFC建立了一个消息映射表(消息流动网),按照消息流动网匹配对应的消息处理函数,看到这里相信你有这样的疑问:序定义了CWinApp类的theApp全局变量,可是从来没有调AfxBeginThread或theApp.CreateThread启动线呀,theApp对应的线是怎么启动的在CWinApp的构造函数里,MFC将theApp"对应"向了这个线,具体的实现是这样的:{if(lpszAppName!=NULL)m_pszAppName=//initializeCWinThreadAFX_MODULE_STATE*pModuleState=ASSERT(AfxGetThread()==NULL);ASSERT(AfxGetThread()==this);m_hThread=::GetCurrentThread();m_nThreadID=::GetCurrentThreadId();//initializeCWinAppstateASSERT(afxCurrentWinApp==NULL);//onlyoneCWinAppobjectpleaseASSERT(AfxGetApp()==this);//innon-runningstateuntilWinMainm_hInstance=NULL;m_pszHelpFilePath=NULL;m_pszProfileName=NULL;m_pszRegistryKey=NULL;m_pszExeName=NULL;m_pRecentFileList=NULL;m_pDocManager=NULL;m_lpCmdLine=NULL;m_pCmdInfo=NULL;//initializewaitcursorstatem_nWaitCursorCount=0;//initializecurrentprinterstatem_hDevMode=NULL;m_hDevNames=NULL; ges=0;//notspecified(defaultsto//initializeDAOm_lpfnDaoTerm=NULL;//willbesetifAfxDaoInit//otherinitialization}很显然,theApp成员变量都被赋予OS启动的这个当前线相关的值,如代码m_hThreadm_hThreadGetCurrentThread();//theApp的线句柄等于当前线句柄m_nThreadID::GetCurrentThreadId();//theApp的线ID等于当前线所以CWinApp类几乎只是为MFC序的第一个线量身定制的,它不需要也不能被AfxBeginThread或theApp.CreateThread"再次"CWinApptheAppUI线,不要继承类CWinApp,而应继承类CWinThread。而参考第1节,由于我们一般以主线(在MFC序里实际上就是OS启动的第一个线)处理所有窗口的消息,所以我们几乎没有再启动UI线在工业控制系统中,工控机(PCWindows平台)经常需要与单片机通过串口进行通信。因此,操作和使用PC的串口成为大多数单片机、嵌入式系统领域工师必须具备的能力。下图从Windows的"控制面板->系统->设备管理器->通信端口(COM1)"打开COM的设置窗口:WIN32平台下,对通信端口进行操作跟基本的文件操作一样。创建/打开COM资源下列函数如果调用成功,则返回一个标识通信端口的句柄,否则返回-WORDdwDesiredAccess,//对资源的 PSECURITY_ATTRIBUTESlpSecurityAttributes,//安全描述符指针,可为NULLWORDdwFlagsAndAttributes,//文件属性,可为NULL teFile//模板文件句柄,置为获得/COM属下列函数可以获得COMBOOLBOOLmState(HANDLEhFile,标识LPDCBlpDCB指向一个设备控制块(DCB结构)如果要调整通信端口的参数,则需要重新配置设备控制块,再用WIN32API mState(HANDLEhFile,标识LPDCBlpDCB指向一个设备控制块(DCB结构)DCB结构包含了串口的各项参数设置,如typedeftypedefstruct{//DWORDDCBlength;//sizeof(DCB)DWORDBaudRate;//currentbaudDWORDfBinary:1;//binarymode,noEOFcheckDWORDfParity:1;//enableparitycheckingDWORDfOutxCtsFlow:1;//CTSoutputflowcontrolDWORDfOutxDsrFlow:1;//DSRoutputflowcontrolDWORDfDtrControl:2;//DTRflowcontroltypeDWORDfDsrSensitivity:1;//DSRsensitivityDWORDfTXContinueOnXoff:1;//XOFFcontinuesTxDWORDfOutX:1;//XON/XOFFoutflowcontrolDWORDfInX:1;//XON/XOFFinflowcontrolDWORDfErrorChar:1;//enableerrorre DWORDfNull:1;//enablenullstripDWORDfRtsControl:2;//RTSflowcontrolDWORDfAbortOnError:1;//abortreads/writesonerrorDWORDfDummy2:17;//WORDw ;//notcurrentlyusedWORDXonLim;//transmitXONthresholdWORDXoffLim;//transmitXOFFthresholdBYTEByteSize;//numberofbits/byte,4-8BYTEParity;//0-4=no,odd,even,mark,spaceBYTEStopBits;//0,1,2=1,1.5,2charXonChar;//TxandRxXONcharactercharXoffChar;//TxandRxXOFFcharactercharErrorChar;//errorre cementcharactercharEofChar;//endofinputcharactercharEvtChar;//receivedeventWORD1;};donot mMask(HANDLEhFile,标识值AbreakwasdetectedonTheCTS(clear-to-send)signalchangedAline-statuserroroccurred.Line-statuserrorsareCE_FRAME,andAringindicatorwasTheRLSD(receive-line-signal-detect)signalchangedAcharacterwasreceivedandcedintheinputTheeventcharacterwasreceivedandcedintheinputbuffer.Theeventisspecifiedinthedevice'sDCBstructure,whichisappliedtoaserialportbyusing mStatefunction.Thelastcharacterintheoutputbufferwas mEvent(HANDLEhFile,标 LPOVERLAPPEDlpOverlapped,//指向overlappedLPVOIDlpBuffer,//输入数据Buffer指针DWORDnNumberOfBytesToRead,//需 的字节LPDWORDlpNumberOfBytesRead,//实 的字节数指LPOVERLAPPEDlpOverlappedoverlappedBOOLWriteFile(HANDLEhFile,//LPCVOIDlpBuffer,BufferDWORDnNumberOfBytesToWrite,//需要写的字节数LPOVERLAPPEDlpOverlapped//指向overlapped结构工实下面我们用第1节所述API实现一个多线的串口通信序。这个例子工(工名)的界面很简单,如下图所它是一个多线的应用序,包括两个工作者线,分别处理串口1和串口2。为了简化问题,我们EV_RXCHAR)。在工实例的BOOL {NULL,OPEN_EXISTING,0,NULL);if(hComm1==(HANDLE)-{returnfalse;}{DCBmState(hComm1,&wdcb);}//打开并设置COM2if(hComm2==(HANDLE)-1){returnfalse;}{DCB}m_pMainWnd=&dlg;if(nResponse==IDOK){// cecodeheretohandlewhenthedialog//dismissedwith}elseif(nResponse=={// cecodeheretohandlewhenthedialog//dismissedwith}return}此后我们在 Dlg的初始化函数OnInitDialog中启动两个分别处理COM1COM2的线 {//Add itemto //IDM_ABOUTBOXmustbeinthesystemcommandASSERT(IDM_ABOUTBOX<0xF000); *pSys =GetSystem if(pSys !=NULL){CString if(!strAbout {--(MF_STRING,IDM_ABOUTBOX,}}//Settheiconforthisdialog.Theframeworkdoesthis//whentheapplication'smainwindowisnotadialogSetIcon(m_hIcon,TRUE);//SetbigiconSetIcon(m_hIcon,FALSE);//Setsmall//TODO:AddextrainitializationDWORDhCommThread1=::CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,if(hCommThread1=={AfxMessageBox("1");returnfalse;}DWORDhCommThread2=::CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,if(hCommThread2=={AfxMessageBox("AfxMessageBox("2");returnfalse;}returnTRUE;//returnTRUEunlessyousetthefocustoa}{DWORDwEven;while{if(wEven=0){hCommThread1=NULL;}{switch{caseif(wTxPos<{DWORDwCount;//写入的字节数}caseif(com1Data.wRxPos<{ 串口数据,DWORDwCount; if(com1Data.wRxPos==if(com1Data.wRxPos==::PostMessage(hWnd,COM_SENDCHAR,0,}}}}}return}{DWORDwEven;while{if(wEven=0){hCommThread2=NULL;}{switch{caseif(wTxPos<{DWORDwCount;//写入的字节数WriteFile(hComm2,com2Data.TxBuf[wTxPos1,&wCount,NULL);}caseif(com2Data.wRxPos<{ 串口数据DWORDwCount; ReadFile(hComm2,com2Data.RxBuf[wRxPos],1,&wCount,}}}}return}线控制函数中所操作的com1Data和com2Data是与串口对应的数据结构structtagSerialPort的实例,typedeftypedefstruct{BYTERxBuf[SPRX_BUFLEN];//接收BufferWORDwRxPos;//当前接收字节位置WORDwRxLen;//要接收的字节数BYTETxBuf[SPTX_BUFLEN];//发送BufferWORDwTxPos;//当前发送字节位置WORDwTxLen;//要发送的字节数使用多线串口通信更方便的途径是编写一个多线的串口类,例如RemonSpekreijse编写了一 串口类。仔细分析这个类的源代码,将十分有助于我们对先前所学多线及同步知识的理解#ifndef#ifndef#defineM_BREAK_DETECTEDWM_USER+1//AbreakwasdetectedonM_CTS_DETECTEDWM_USER+2//TheCTS(clear-to-send)signalM_DSR_DETECTEDWM_USER+3//TheDSR(data-set-ready)signalM_ERR_DETECTEDWM_USER+4//Aline-statuserroroccurred.Line-statuserrorsareCE_FRAME,CE_OVERRUN,andCE_RXPARITY.M_RING_DETECTEDWM_USER+5//AringindicatorwasM_RLSD_DETECTEDWM_USER+6//TheRLSD(receive-line-signal-detect)signalchangedstate.M_RXCHARWM_USER+7//Acharacterwasreceived cedinthe M_RXFLAG_DETECTEDWM_USER+8//Theeventcharacterwasreceivedcedintheinput M_TXEMPTY_DETECTEDWM_USER+9//Thelastcharacterintheoutputbufferwassent.class{vir
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 棉花加工工安全实践竞赛考核试卷含答案
- 八年级物理下册计算综合复习专题教学设计
- 钨钼冶炼工岗前班组评比考核试卷含答案
- 小学四年级心理健康帮助他人收获快乐教学设计
- 高三英语高考听力标准训练(第十七套)听前预测与听中抓点教学设计
- 2026届初中道德与法治中考冲刺三轮复习教学设计
- 高一数学《集合与常用逻辑用语》复习课教学设计:核心素养视域下逻辑推理与集合运算的深度融合
- 浆纱机操作工安全强化竞赛考核试卷含答案
- 骨灰管理员诚信道德能力考核试卷含答案
- 焦炉炉前工安全意识知识考核试卷含答案
- 2026年强制性产品认证应知应会培训考核卷(十一)
- GB 24284-2026大型焰火燃放安全技术规程
- 自考00277行政管理学重点复习资料
- (零模)苏州市2027届高三年级9月阳光调研试卷 生物试卷(含答案)
- 血常规解读:从化验单到临床线索
- 2026年江苏省泰州市抗震办公室(审图中心)招聘1人易考易错模拟试题(共500题)试卷后附参考答案
- 2026年世界职业院校技能大赛“智能网联汽车技术组”参考试题及答案
- 2025年江苏泰州市中考语文试卷真题及答案详解(精校打印)
- 婴幼儿伤害预防与处理配套教材电子课件(完整版)
- (2026年)护理部垂直管理及精细化管理课件
- 2026年殡葬系统版遗体火化师技能知识试题
评论
0/150
提交评论