
可视化程序设计第7章多线程程序设计 线程与泛型的构建 进程和线程 线程的建立 线程的控制 线程的挂起 线程的恢复 线程的终止 线程的多核调用 线程之间数据共享 线程同步与互斥 多线程之间的竞争 使用临界区对象实现泛型竞争 使用互斥量实现泛型竞争 使用信号量对象实现线程同步 使用事件对象实现线程同步 使用消息实现泛型之间的通信 进程和线程 线程在这里分为两种,一个是工作线程,一个是用户界面线程 运行状况就是进程正在执行的状况,就绪状态就是进程等待执行的状况,阻塞状态是进程等待某一事件出现的状况,这个事件或许是输入输出的完成,也或许是其它进程完成某项任务(后面描述)。进程可以从运行状况到阻塞状态是期待某一事件的出现,当期待的事件出现之后,进程都会从阻塞状况前往就绪状态。当进程运行一段时间后,到达它还能大幅运行的时间后(时间片),进程就会从运行状况变为就绪状态。Windows系统进程调度使用的是优先级调度,优先级高的才会优先运行。 线程的建立 线程类及其原则。VC中控制线程的类为CWinThread。 CWinThread类的可重载方式 线程优先级 工作线程 建立方法如下: UINT ThreadWork(LPVOID pParam){ …… return 0; } CWinThread* AfxBeginThread( //线程执行体函数指针 AFX_THREADPROC pfnThreadProc, LPVOID pParam, //向线程传递参数 //线程的优先级 int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0,//线程所占堆栈的型号 DWORD dwCreateFlags = 0,//线程创建标志 //线程的安全结构 LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL) pfnThreadProc是变量指针ccriticalsection 调用 2次,它指向静态或一个全局变量,而这个数组就是线程的执行体。

pParam是一个指针参数,它可以指向任何类别数据,如整数,句柄和结构体等。 nStackSize是泛型运行时使用堆栈的空间,如果为0,就是默认值1M。1M的空间对于通常的线程尚未足够大了。 dwCreateFlags : 指定创建线程之后,线程有怎么样的标志。可以指定两个值:: CREATE_SUSPENDED : 线程创建之后,会进入挂起状态,直到读取:ResumeThread 0:创建线程后就起初运行. lpSecurityAttrs:指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标识新建立线程的安全性。如果为 NULL,那么新建立的轮询就具备和主线程一样的安全性。 用户界面线程 使用向导可以生成用户界面线程。 生成对话框作为用户界面线程的图标。 建立用户界面线程 CWinThread* AfxBeginThread ( CRuntimeClass* pThreadClass,//用户界面线程 int nPriority = THREAD_PRIORITY_NORMAL,//优先级 UINT nStackSize = 0,//堆栈尺寸 DWORD dwCreateFlags = 0,//线程创建标志 LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL //线程的安全结构 ) 线程的挂起 调用SuspendThread函数用于将泛型的挂起计数加1,当线程的挂起计数大于0时,该线程将暂停执行,称之为挂起状态。

SuspendThread最好在泛型内部读取ccriticalsection 调用 2次,这样可以了解泛型什么时候停止减少导致宕机。而死锁是轮询进入阻塞状况后,永远也得不到执行的一种状态。SuspendThread的返回值是挂起的计数值。如果返回-1,就会意味着发生错误。 线程的恢复 调用消耗线程挂起的ResumeThread用于将线程的挂起计数减1,当线程的挂起计数等于0时,线程恢复执行。 在程序设计中通常不建议使用SuspendThread和ResumeThread实现线程的操作。只是程构建过程中必须完成某些初始化工作,首先使线程暂停操作,然后读取ResumeThread恢复线程的运行。 线程的中止 线程变量退出循环来返回 通过读取ExitThread 函数,线程将自行撤消 void ExitThread ( DWORD dwExitCode ) 同一个进程或另一个进程中的轮询读取TerminateThread 函数 BOOL TerminateThread ( HANDLE hThread,//线程子类 DWORD dwExitCode//退出代码 ) 在进程中止运行时撤消线程 线程的多核调用 DWORD_PTR SetThreadAffinityMask ( HANDLE hThread,//线程数组 DWORD_PTR dwThreadAffinityMask//亲和性掩码 ) //线程0运行在CPU0上 SetThreadAffinityMask(hThread0, 0; //线程1, 2, 3运行在CPU1,CPU2,CPU3的任意一个核上 SetThreadAffinityMask(hThread1, 0x0000000E); SetThreadAffinityMask(hThread2, 0x0000000E); SetThreadAffinityMask(hThread3, 0x0000000E); 线程之间数据共享 如在A文件中申明如下: extern int a; 在B文件中申明如下: int a; HANDLE CreateFileMapping ( HANDLE hFile,//文件句柄 LPSECURITY_ATTRIBUTES lpAttributes,// 安全描述符 DWORD flProtect,//保护方式 DWORD dwMaximumSizeHigh,//最大尺寸的高32位 DWORD dwMaximumSizeLow,//最大尺寸的低32位 LPCTSTR lpName// 对象名 ) LPVOID MapViewOfFile ( HANDLE hFileMappingObject,// 文件映射对象 DWORD dwDesiredAccess,//文件访问模式 DWORD dwFileOffsetHigh,//文件映射起点的高32位 DWORD dwFileOffsetLow,//文件映射起点的低32位 SIZE_T dwNumberOfBytesToMap// 映射文件的字符数 ) …… HANDLE hMappingSend; hMappingSend=CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0,8*1024,"SENDDATE"); LPSTR lpSender; lpSender=(LPSTR)MapViewOfFile(hMappingSend,FILE_MAP_ALL_ACCESS,0,0,0); …… 多线程之间的竞争 如存在两个线程TA、TB和一个共享的全局变量a,TA访问共享函数的程序如下: a=a+1 TB的如下: a=a+2 a的初始值为2,不论先执行TA还是TB最终a的值都需要是5。

但是有的状况下只是这么,因为a=a+1编译后为下面三句: regester←a① regester=regester+1② a←regester③ a=a+2编译后为下面三句: regester←a① regester=regester+2② a←regester③ 使用临界区对象实现泛型竞争 可以使用CCriticalSection(临界区类)实现这些互斥访问。 class CCriticalSection :public CSyncObject { public: CCriticalSection(); virtual BOOL Unlock( ); BOOL Lock( ); BOOL Lock(DWORD dwTimeout ); } CCriticalSection criticalSection; 对于TA线程 While(1){ criticalSection.Lock(); a=a+1; criticalSection.Unlock(); } 对于TB线程 While(1){ criticalSection.Lock(); a=a+2; criticalSection.Unlock(); } 使用互斥量实现泛型竞争 class CMutex:public CSyncObject { public: CMutex (BOOL bInitiallyOwn=FALSE, LPCTSTR lpszName=NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL ); } 该类只有一个构造函数,该构造函数存在参数。
bInitiallyOwn说明初始条件下,该类的对象进入锁定状态。 lpszName该平台对象的名称。如果指明了该对象的名称就可以推动进程之间的共享。 lpsaAttribute该对象安全描述符的属性,一般为NULL。 CMutex mutex;//定义一个CMutex类型的全局函数。 对于TA线程 While(1){ mutex.Lock(); a=a+1; mutex.Unlock(); } 对于TB线程 While(1){ mutex.Lock(); a=a+2; mutex.Unlock(); } HANDLE hMappingSend; //创建存储映射文件 hMappingSend=CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0,4, "SENDDATE");//把数据映射为整数 int *a=(int *)MapViewOfFile(hMappingSend,FILE_MAP_ALL_ACCESS,0,0,0); CMutex mutex(FALSE, “SendMutex”);//生成名称为“SendMutex”的一个互斥量 While(1){ mutex.Lock(); *a=*a+1; mutex.Unlock(); } 对于TB线程 HANDLE hMappingSend; hMappingSend=CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0,4, "SENDDATE"); int *a=(int *)MapViewOfFile(hMappingSend,FILE_MAP_ALL_ACCESS,0,0,0); CMutex mutex(FALSE, “SendMutex”); While(1){ mutex.Lock(); *a=*a+2; mutex.Unlock(); } 使用信号量对象实现线程同步 CSemaphore:public CSyncObject { CSemaphore(LONG lInitialCount=1, LONG lMaxCount=1, LPCTSTR pstrName=NULL,LPSECURITY_ATTRIBUTES lpsaAttributes = NULL ); } lInitialCount是信号量的初始值。
默认为1,相当于互斥量CMutex。表示资源的数量。 lMaxCount是信号量的最大值。为资源的最大数目。 pstrName是信号量的名称。 lpsaAttributes是该信号量的安全属性,一般为NULL。 使用事件对象实现线程同步 class CEvent { public: CEvent( BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL ); BOOL SetEvent(); BOOL PulseEvent( ); BOOL ResetEvent( ); virtual BOOL Unlock( ); } 其中CEvent构造函数中bInitiallyOwn表示初始化时的状况,默认是非激发状态。bManualReset说明时间能否自动。lpszName不为空时,改事件对象就可以跨进程使用。 DWORD WaitForSingleObject ( HANDLE hHandle,//等待事件句柄 DWORD dwMilliseconds//等待的最长时间 ); hHandle事件句柄代表事件、互斥量、信号量、线程和进程。
事件对象存在两种类别:手动类型和自动种类。手动类型是由手工调用SetEvent()和ResetEvent()函数对事件修改或释放状态。自动状态是由事件自动由信号态返回到非信号态,PulseEvent()选择一个等待自动类型事件的轮询或进程运行,并使事件自动由信号态返回到非信号态。 使用消息实现线程之间的通信 LRESULT SendMessage ( HWND hWnd,// 目标窗口句柄 UINT Msg,// 消息 WPARAM wParam,// 第一个消息参数 LPARAM lParam// 第二个消息参数 ); SendMessage函数把消息直接发送给线程的消息队列,并期待消息处理函数的处理,当消息处理变量完成处理后,调用返回。 BOOL PostMessage ( HWND hWnd,//目标窗口句柄 UINT Msg,//消息 WPARAM wParam,//第一个消息参数 LPARAM lParam//第二个消息参数 ); PostMessage函数只是把消息直接发送到消息队列,它并不等待消息处理函数处理就直接返回。 SendMessage和PostMessage主要差别就是:前者是阻塞调用,必须等待消息处理函数处理完成后再返回;后者是非阻塞调用,只把消息挂在消息队列中。
BOOL PostThreadMessage ( DWORD idThread,// 线程号 UINT Msg,// 消息 WPARAM wParam,//第一个消息参数 LPARAM lParam//第二个消息参数 ) BOOL PeekMessage ( LPMSG lpMsg,// 消息 HWND hWnd,// 窗口句柄 UINT wMsgFilterMin,// 索引消息号的最小值 UINT wMsgFilterMax,// 索引消息号的最大值 UINT wRemoveMsg// 删除选项 ); * * 指向容器应用程序的主窗口,当一个OLE服务器被现场激活时 m_pActiveWnd 保存指向应用程序的主窗口的指针 m_pMainWnd 当前线程的ID,只有线程运行时才有效 m_nThreadID 当前线程的键值,只有轮询运行时才有效 m_hThread 指定线程结束时是否要销毁对象 m_bAutoDelete 说明 属性 可重载以定制缺省的消息循环 Run 截获线程消息和命令处理数组出现的所有未处理的异常 ProcessWndProcException 检测特定的消息 IsIdleMessage 在特定的消息前往应用程序之前截获消息 ProcessMessageFilter 过滤消息 PreTranslateMessage 重载以进行轮询特定的空闲操作 OnIdle 重载以实现泛型例子的初始化 InitInstance 重载以进行线程中止时的清理工作 ExitInstance 说明 成员函数 空闲优先级 THREAD_PRIORITY_IDLE 比普通优先级低两个单位 THREAD_PRIORITY_LOWEST 比普通优先级低一个单位 THREAD_PRIORITY_BELOW_NORMAL 普通优先级 THREAD_PRIORITY_NORMAL 比普通优先级高一个单位 THREAD_PRIORITY_ABOVE_NORMAL 比普通优先级高两个单位 THREAD_PRIORITY_HIGHEST 实时优先级 THREAD_PRIORITY_TIME_CRITICAL 说明 优先级 文件可写 FILE_MAP_ALL_ACCESS 映射只读 FILE_MAP_READ 映射可读可写 FILE_MAP_WRITE 说明 文件访问路径值
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-125829-1.html
原来钱真的不是万能哦
地产商不行贿能拿到地吗