c多线程编程,c多线程编程实例,VC多线程编程详解本文主要介绍了VC多线程编程,并以实例的形式详细分析了多线程编程的原理和实现方法。有一定的参考价值,有需要的朋友可以参考一下。本文
c 多线程编程,c多线程编程实例,VC多线程编程详解
本文主要介绍了VC多线程编程,并以实例的形式详细分析了多线程编程的原理和实现方法。有一定的参考价值,有需要的朋友可以参考一下。
本文用实例讲述VC多线程编程的概念和技巧,与大家分享,供大家参考。具体分析如下:
一、多线程编程要点
它是一个线程进程的执行路径,包含独立的堆栈和CPU寄存器状态。每个线程共享所有进程资源,包括打开的文件、信号标识和动态分配的内存。一个进程中的所有线程都使用相同的地址空间,这些线程的执行由系统调度程序控制,系统调度程序决定哪个线程可以执行以及何时执行。有一个线程优先级,优先级较低的线程必须等到优先级较高的线程执行完。在多处理器机器上,调度器可以将多个线程放在不同的处理器上运行,这样可以平衡处理器的任务,提高系统的运行效率。
Windows是一个多任务操作系统,在一个Windows进程中包含一个或多个线程。32位Windows环境下的Win32 API提供了多线程应用程序开发所需的接口函数,VC中提供的标准C库也可以用来开发多线程应用程序。相应的MFC类库封装了多线程编程类,用户可以根据应用的需求和特点选择相应的工具。为了让大家充分了解Windows多线程编程技术,本文将重点介绍如何在Win32 API和MFC中编写多线程程序。
Win32模式下多线程编程的原理与MFC类库支持的一致,一个进程的主线程可以随时创建一个新线程。当线程结束时,自动终止线程;当进程结束时,所有线程都被终止。所有活动线程共享进程的资源,所以在编程时需要考虑多个线程访问同一个资源时的冲突问题。当一个线程正在访问一个进程对象,而另一个线程想要改变该对象时,可能会产生错误的结果。这种冲突应该在编程时解决。
二、Win32 API下的多线程编程
Win32 API是Windows操作系统内核和应用程序之间的接口。它封装了内核提供的函数,应用程序通过调用相关函数获得相应的系统函数。为了给应用程序提供多线程功能,Win32 API函数集提供了一些处理多线程程序的函数集。用Win32 API直接编程有很多优点:基于Win32的应用程序执行代码小,运行效率高,但需要程序员编写更多的代码,管理系统为程序提供的所有资源。直接用Win32 API编写程序,需要程序员对Windows系统内核有一定的了解,这将占用程序员大量的时间来管理系统资源,从而降低了他们的工作效率。
1. 用Win32函数创建和终止线程
Win32库提供操作多线程的函数,包括创建线程、终止线程和建立互斥。在应用程序的主线程或其他活动线程中创建新线程的功能如下:
如下:Handle CreateThread(LP security _ attributes LP thread属性,dword dwstack大小,LP thread _ start _ routine LP start address,lpvoid lpparameter,dword dwcreationflags,lpd word LP threadid);
如果创建成功,则返回线程的句柄;否则,返回NULL。创建新线程后,该线程开始执行。但是,如果在dwCreationFlags中使用了CREATE_SUSPENDED特性,那么线程不会立即执行,而是先挂起,然后调用ResumeThread后再启动线程。在这个过程中,可以调用下面的函数来设置线程的优先级:
如下:Bool Sethread Priority(handle hthread,int n Priority);
当调用线程的函数返回时,线程自动终止。如果需要在线程执行期间终止线程,可以调用函数:
如下:VOID exit thread(DWORD dwExitCode);
如果线程在线程外终止,可以调用下面的函数:
如下:Bool terminate thread(处理hthread,dword dwexit代码);
但需要注意的是,该功能可能会造成系统不稳定,线程占用的资源不会被释放。因此,一般情况下,不建议使用该功能。
如果要终止的线程是进程中的最后一个线程,则应该在线程终止后终止相应的进程。
2. 线程的同步
在线程体中,如果线程是完全独立的,并且在数据访问等资源操作上与其他线程没有冲突,那么可以按照通常的单线程方式进行编程。但是,在多线程中往往不是这样,线程经常要同时访问一些资源。访问共享资源引起的冲突是不可避免的。为了解决这个线程同步问题,Win32 API提供了多种同步控制对象,帮助程序员解决共享资源访问冲突。在介绍这些同步对象之前,先介绍一下等待函数,因为这个函数是用于所有控制对象的访问控制的。
Win32 API提供了一组等待函数,可以让线程阻塞自己的执行。这些函数在其参数中从一个或多个同步对象生成信号,否则它们将只在指定的等待时间后返回。等待函数不返回时,线程处于等待状态,此时线程只消耗一点点CPU时间。等待功能不仅可以保证线程的同步,还可以提高程序的运行效率。最常用的等待功能有:
如下:DWORD waitforsingleobject(handle h handle,DWORD dw milliseconds);
WaitForMultipleObject函数可用于同时监视多个同步对象。这个函数的声明是:
如下:Dword Waitformula对象(Dword ncount,const handle * LP handles,bool bwaitall,Dword dw millions);
(1)互斥体对象
当互斥对象不被任何线程拥有时,它的状态有一个信号,但当它被拥有时没有信号。互斥对象适用于协调多个线程对共享资源的互斥访问。您可以按如下方式使用该对象:
首先,建立互斥对象以获得句柄:
如下:HANDLE create mutex();
然后,在线程可能发生冲突的区域之前(即访问共享资源之前)调用WaitForSingleObject,将句柄传递给函数,请求占用互斥对象:
如下:dwwaitresult=waitforsingleobject(hmutex,5000 l);
共享资源访问结束,释放互斥对象的占用:
如下:release mutex(hMutex);
一个互斥对象一次只能被一个线程占用。当一个互斥对象被一个线程占用时,如果另一个线程想占用它,必须等到前一个线程被释放后才能成功。
(2)信号对象
Signal允许多个线程同时访问共享资源,并指定创建对象时可以同时访问的最大线程数。当线程成功申请访问时,signal对象中的计数器减一,调用ReleaseSemaphore函数后,signal对象中的计数器加一。其中计数器值大于或等于0,但小于或等于创建时指定的最大值。如果应用程序在创建信号对象时将其计数器的初始值设置为0,它会阻塞其他线程并保护资源。初始化完成后,调用ReleaseSemaphore函数将其计数器增加到最大值,即可进行正常访问。您可以按如下方式使用该对象:
首先,创建信号对象:
如下:HANDLE CreateSemaphore();
或者打开一个信号对象:
如下:HANDLE open semaphore();
然后,在线程访问共享资源之前调用WaitForSingleObject。
共享资源访问完成后,应该释放对信号对象的占用:
如下:release semaphore();
(3)事件对象
事件是最简单的同步对象,包括有信号和无信号两种状态。在线程访问资源之前,它需要等待事件发生。这时,使用事件对象是最合适的。例如,只有在通信端口缓冲器中接收到数据之后,监控线程才被激活。
该事件是用CreateEvent函数创建的。这个函数可以指定事件对象的类和事件的初始状态。如果是手动复位事件,它将始终保持信号状态,直到使用复位事件功能将其复位为非信号事件。如果是自动复位事件,在单个等待线程被释放后,其状态会自动变为静默。SetEvent可用于将事件对象设置为信号状态。创建事件时,可以命名对象,以便其他进程中的线程可以使用OpenEvent函数打开具有指定名称的事件对象的句柄。
(4)排斥区对象
在排除区异步执行时,只能在同一个进程的线程间共享资源处理。虽然此时可以使用上述几种方法,但是使用禁区的方法使同步管理更加有效。
使用时,先定义一个CRITICAL_SECTION结构排除区对象,在使用进程前调用以下函数初始化对象:
如下:void初始化器临界段(LP critical _ section);
当一个线程使用exclusion zone时,调用函数:EnterCriticalSection或TryEnterCriticalSection
当需要占用或退出禁区时,调用函数LeaveCriticalSection释放对禁区对象的占用,供其他线程使用。
三、基于MFC的多线程编程
它是微软的VC开发集成环境中提供给程序员的基础函数库。它将Win32 API封装在类库中,以类的形式提供给开发者。由于其快速、简单、功能强大的特点,深受开发者的喜爱。所以应用开发推荐使用MFC类库。
VC附带的MFC类库提供了对多线程编程的支持,基本原理与基于Win32 API的设计一致。但是,因为MFC封装了同步对象,所以实现起来更方便,并且避免了对象句柄管理的繁琐工作。
在MFC中,有两种线程:工作线程和用户界面线程。工作线程与前述线程相同,用户界面线程是可以接收用户输入并处理事件和消息的线程。
1. 工作线程
工作线程编程比较简单,设计思路基本和上面说的一样:一个基本函数代表一个线程,线程创建启动后,线程进入运行状态;如果线程使用共享资源,它们需要同步资源。通过这种方式,可以在线程创建和启动时调用该函数:
如下:CWinThread*AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int n PRIORITY=THREAD _ PRIORITY _ NORMAL,
UINT nStackSize=0,
DWORD dwCreateFlags=0,
LP security _ ATTRIBUTES lpSecurityAttrs=NULL);
参数pfnThreadProc是一个线程执行器函数,其原型是uint线程函数(lpvoid p param)。
参数pParam是传递给执行函数的参数;
参数nPriority是线程执行权限,可选值:
线程优先级正常、线程优先级最低、线程优先级最高、线程优先级空闲。
参数dwCreateFlags是创建线程时的标志。可以取CREATE_SUSPENDED的值,表示线程创建后处于挂起状态,调用ResumeThread函数后线程继续运行,或者取值“0”表示线程创建后处于运行状态。
返回值是CWinThread类对象的指针,其成员变量m_hThread是线程句柄。在Win32 API模式下,线程操作的函数参数需要线程句柄,所以线程创建后,所有Win32 API函数都可以用来对pWinThread-m_Thread线程执行相关操作。
注意:在类对象中创建和启动线程时,线程函数应该定义为类外的全局函数。
2. 用户接口线程
基于MFC的应用程序有一个application对象,它是CWinApp派生类的一个对象,这个对象代表应用程序进程的主线程。当线程完成执行并退出线程时,进程自动结束,因为进程中没有其他线程。CWinApp类派生自CWinThread,是用户界面线程的基类。当我们编写用户界面线程时,我们需要从CWinThread中派生出自己的线程类,而ClassWizard可以帮助我们做到这一点。
首先,使用ClassWizard派生一个新类,并将基类设置为CwinThread。注意:类的DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏是必需的,因为类的对象需要在创建线程时动态创建。如果需要,初始化和结束代码可以分别放在该类的InitInstance和ExitInstance函数中。如果你需要创建一个窗口,你可以在InitInstance函数中完成。然后创建一个线程并启动它。有两种方法可以创建用户界面线程。MFC提供了两个版本的AfxBeginThread函数,其中一个用于创建用户界面线程。第二种方法分两步:首先调用thread类的构造函数创建一个thread对象;其次,调用CWinThread:CreateThread函数来创建线程。在线程建立和启动之后,它在线程功能的执行期间保持有效。如果它是一个线程对象,线程在对象被删除之前被终止。CWinThread已经为我们完成了线程结束工作。
3. 线程同步
前面我们介绍了Win32 API提供的几个与线程同步相关的对象,这些对象被类封装在MFC类库中。它们有一个共同的基类CSyncObject,对应关系为:Semaphore对应CSemaphore,Mutex对应CMutex,Event对应ce Event,CriticalSection对应CCriticalSection。此外,MFC还封装了两个等待函数,分别是CSingleLock和CMultiLock。由于四个对象的用法相似,这里以CMutex为例进行说明:
创建CMutex对象:
如下:CMutex互斥体(FALSE,NULL,NULL);
或者复制如下代码:CMutex互斥体;
当每个线程都想要访问共享资源时,请使用以下代码:
如下:CSingleLock sl(互斥);
sl。lock();
如果(sl。IsLocked())
//在共享资源上操作.
sl。unlock();
四、结束语
如果用户的应用需要同时处理多个任务,多线程是理想的选择。在这里提醒一下,多线程编程的时候要注意资源共享和多线程调试的问题。
希望这篇文章对大家的VC编程有所帮助。