190 likes | 332 Views
Windows 编程技术. 多任务. 概述. Windows 是一个抢先式多任务操作系统。所谓多任务是指它可以允许多个进程同时运行,而每个进程又由一个或多个线程组成,这些线程也可以同时运行,所谓抢先是指操作系统可以在任何时候挂起当前进程或线程的执行,把 CPU 控制权转交给别的进程或线程继续执行。 在操作系统中,一个进程通常就是一个运行着的程序,它可以由一个或多个模块组成,每个模块通常对应磁盘上的一个文件,如 EXE 或 DLL 文件。而一个线程通常就是这个程序中可以并行运行的某一个全局函数。本章将讲述进程和线程的使用方法。. 进程. 进程的优先级
E N D
Windows编程技术 多任务
概述 • Windows是一个抢先式多任务操作系统。所谓多任务是指它可以允许多个进程同时运行,而每个进程又由一个或多个线程组成,这些线程也可以同时运行,所谓抢先是指操作系统可以在任何时候挂起当前进程或线程的执行,把CPU控制权转交给别的进程或线程继续执行。 • 在操作系统中,一个进程通常就是一个运行着的程序,它可以由一个或多个模块组成,每个模块通常对应磁盘上的一个文件,如EXE或DLL文件。而一个线程通常就是这个程序中可以并行运行的某一个全局函数。本章将讲述进程和线程的使用方法。
进程 • 进程的优先级 一个进程(process)是应用程序的一个运行实例,进程并不一定都有窗口或显示在屏幕上,每个进程都有自己的内存空间、代码、数据和系统资源。一个进程在运行的过程中创建的资源随着进程的终止而被销毁,分配的系统资源在进程终止时被释放或关闭。 在Windows系统中,对每个进程可设置不同的优先级,Windows系统内部的调度程序根据进程的优先级来酌情分配CPU时间以使进程运行。一般情况下,进程优先级应被设置成标准级NORMAL_PRIORITY_CLASS,教材表14-1是Windows定义的进程优先级类型。
进程 • 启动进程 • CreateProcess() 该函数是基本的启动进程函数,其函数原型定义如下: BOOL CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation );
进程 • CreateProcess示例 以下程序代码启动注册表程序: STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); if(!CreateProcess(NULL,"regedit",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)) { AfxMessageBox( "建立进程失败!" ); }
进程 • WinExec()启动进程 该函数内部调用CreateProcess()启动进程,其原型定义如下: UINT WinExec(LPCSTR lpCmdLine,UINT uCmdShow); 参数lpCmdLine为命令行参数。 参数uCmdShow为窗口的样式,该值用于ShowWindow (HWND hwnd,int nCmdShow)中的nCmdShow来显示窗口,参见本书“1.3简单的Windows程序介绍”一节内容。 以下程序代码启动注册表程序: if(WinExec("regedit.exe",SW_SHOWNORMAL)) { AfxMessageBox( "建立进程失败!" ); } • C/C++语言中的启动进程函数启动进程
进程 • 进程的管理 • 取得当前进程的句柄及ID • GetCurrentProcess • DuplicateHandle • GetCurrentProcessID • 取得其它进程的句柄及ID • CreateToolhelp32Snapshot • Process32First • Process32Next • OpenProcess
进程 • 取得和设置进程的优先级 • 取得进程优先级,hProcess为目标进程句柄: DWORD GetPriorityClass(HANDLE hProcess); • 设置进程优先级,hProcess为进程句柄:dwPriorityClass为进程新的优先级: DWORD SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass); • 终止进程 • 在一进程任务完成后,可以调用函数ExitProcess()结束进程: VOID ExitProcess(UINT uExitCode) • 函数TerminateProcess() 可以结束调用进程和其它进程: BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode);
线程 • 线程的优先级 线程(Thread)是进程内部的一个可执行路径。例如,启动“记事本”程序时,操作系统将创建进程并开始执行进程的主线程(primary thread)。所有进程至少拥有一个线程,即主线程,主线程终止,进程终止。多个线程可以并发或并行的运行于同一个进程之中,每个线程都共享它们的进程的内存空间、全局变量和系统资源等。 同进程一样,线程也有优先级。Windows定义了线程的优先级类型,合理使用这些优先级可以改善程序性能。例如,通常从调制解调器接收数据的线程比需要从磁盘文件读取数据的进程更高的优先级,因为从调制解调器接收数据的线程必须及时接收传来的数据,而磁盘文件上的数据一直都有。教材表14-7列举了线程优先级类型及其说明。
线程 • 线程的创建和终止 • 线程的创建 • MFC使用AfxBeginThread()函数在创建线程 • Windows API使用CreateThread()函数创建线程。 • 为线程函数,它的原型必须按照如下方式定义: UINT ThreadProc(LPVOID pParam); • 线程的终止 • 工作者线程,退出线程函数的同时即可正常终止线程 。 • AfxEndThread()、ExitThread()或者使用return(即线程隐含调用了ExitThread)来终止所在的线程。 • TerminateThread() 终止线程。
线程 • 工作者线程 • 工作者线程不处理Windows消息,通常比用户接口线程简单。 • 创建一个工作者线程。 CWinThread* thread = AfxBeginThread(ThreadProcedure, pParam); • 线程函数代码示例 UINT WorkerThread(LPVOID pParam) { char* pMessage = (char *)pParam; AfxMessageBox(pMessage); return 0; } • 启动线程代码示例 AfxBeginThread(WorkerThread, "工作者线程运行!");
线程 • 用户接口线程 • 用户接口线程含有消息循环,通常较工作者线程复杂。 • 从CWinThread类派生一个线程类, • 重载InitInstance()函数,执行初始化任务。 • 重载ExitInstance()函数,清除自身。 • 代码示例: • 添加CWinThread 派生类CMyThread • 修改CWinThread类的InitInstance()函数 BOOL CMythread::InitInstance() { CFrameWnd* pFrameWnd = new CFrameWnd(); pFrameWnd->Create(NULL, “Thread Window”); //创建线程窗口 pFrameWnd->MoveWindow(0,0,150,150); … return TRUE; } • 启动线程 AfxBeginThread(RUNTIME_CLASS(CMyThread));
进程、线程间的同步 • 概述 • 同步技术可以解决多个进程、线程在访问共享资源时发生冲突问题。 • 假设两个线程同时访问一个共享数据,甲线程进行数据修改,乙线程进行数据读取。由于Windows为抢先式多任务操作系统,当甲线程在数据修改到一半时,系统可能会挂起甲线程,让乙线程运行,乙线程将读到错误数据,导致程序运行出错。 • 为了避免冲突,Windows系统内部定义了四个同步对象和一组等待函数,合理的使用它们可以解决多进程、多线程间的同步问题。 • MFC中CEvent类、CCriticalSection类、CMutex类和CSemaphore类封装了四个同步对象的操作,这4个类从一个纯虚基类CSyncObject派生,CSyncObject类包含两个重要函数: virtual BOOL Lock( DWORD dwTimeout = INFINITE ); 该函数判断同步对象是否可用,如果可用获得对象 virtual BOOL Unlock() 该函数释放调用线程所拥有的同步对象,
进程、线程间的同步 • 等待函数 • WaitForSingleObject 该函数挂起当前线程,直到满足条件后才返回。它的原型定义如下: DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); 参数hHandle是同步对象句柄,对象都被激活后返回WAIT_OBJECT_0; 参数dwMilliseconds以毫秒为单位,如果为0,那么函数就测试同步对象的状态并立即返回,如果为INFINITE,则超时间隔是无限的。 • WaitForMultipleObjects • 该函数挂起当前线程,直到满足条件后才返回。它的原型定义如下: DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles , BOOL bWaitAll,DWORD dwMilliseconds ); 参数nCount是句柄数组中句柄的数目。 参数lpHandles代表一个句柄数组。 参数bWaitAll说明了等待类型
进程、线程间的同步 • 使用事件CEvent MFC的CEvent类封装事件的操作。通常当应用程序必须等到发生某事才能访问资源时,使用 CEvent对象。 • CCriticalSection • CMutex • CSemaphore
进程、线程间的同步 • 使用事件CEvent • 用法 MFC的CEvent类封装事件的操作。通常当应用程序必须等到发生某事才能访问资源时,使用 CEvent对象。 • 状态 • 活动(signal state) • 沉寂 (nosignal state) • 示例 #include <afxmt.h> //包含头文件 CEvent Event; //建立沉寂的、自动的全局CEvent对象,用于线程同步 CString csString; //建立全局CString对象,用于存储字符串信息 UINT Thread(LPVOID pParam) //显示字符串信息的线程 { while(1) { Event.Lock(); //或WaitForSingleObject(Event,INFINITE); //等待Event被激活,当函数返回后Event将自动设为沉寂 AfxMessageBox(csString); } }
进程、线程间的同步 • 使用临界段 CCriticalSection • 用法 CCriticalSection类封装临界段的操作 。通常当一个应用程序的多个线程需要访问同一资源时,使用 CCriticalSection对象。 • 状态 • 解锁 • 加锁 • UnLock()、Lock()进行设置 • 示例 CCriticalSection CriticalSection; //建立全局CCriticalSection对象,解锁状态 UINT Thread1(LPVOID pParam) //线程1 { while(1) { CriticalSection.Lock(); //等待CCriticalSection被解锁,//当函数返回后CriticalSection将被锁定 AfxMessageBox("线程1占有临界段,按<确定>放弃临界段"); CriticalSection.Unlock(); } }
进程、线程间的同步 • 使用互斥量 CMutex • 用法 互斥量与临界段功能基本相同。区别在于:临界段只能用于进程内的同步,互斥量可以用于多个进程间的同步。 • 状态 • 解锁 • 加锁 • UnLock()、Lock()进行设置 • 示例 CMutex Mutex(FALSE,"Mutex"); UINT Thread(LPVOID pParam) { while(1) { Mutex.Lock(); AfxMessageBox("线程占有临界段,按<确定>放弃临界段"); Mutex.Unlock(); } }
进程、线程间的同步 • 使用信号量 CSemaphore • 用法 信号量能够限制应用程序中访问同一资源的线程数。 • 状态 • UnLock()、Lock()进行设置 • 示例 CSemaphore Semaphore(3,3,"Semaphore"); UINT Thread(LPVOID pParam) { int ID=IDOK; while(ID==IDOK) { Semaphore.Lock(); ID=AfxMessageBox("<确定>放弃信号量,<取消>退出线程", MB_OKCANCEL); Semaphore.Unlock(); } return 0; }