本文实例讲述了VC多线程编程的概念和技巧,分享给大家,供大家参考。 具体分析如下:
1.多线程编程要点
线程是进程的执行路径。 它包含独立的堆栈和CPU寄存器状态。 每个线程共享所有进程资源,包括打开的文件、信号标识符和动态分配的内存。 进程中的所有线程都使用相同的地址空间,这些线程的执行由系统调度程序控制,系统调度程序决定哪些线程可以执行以及何时执行。 线程具有优先级,优先级较低的线程必须等待,直到优先级较高的线程完成执行。 在多处理器机器上,调度器可以把多个线程运行在不同的处理器上,这样可以平衡处理器任务,提高系统的运行效率。
它是一种多任务操作系统,在一个进程中包含一个或多个线程。 32位环境下的Win32 API提供了多线程应用程序开发所需的接口函数,也可以使用VC中提供的标准C库来开发多线程应用程序。 相应的MFC类库封装了多线程编程类。 用户在开发时可以根据应用的需求和特点选择相应的工具。 为了使大家能够全面了解多线程编程技术,本文将重点介绍如何通过Win32 API和MFC两种方式编写多线程程序。
Win32模式下的多线程编程原理与MFC类库支持的多线程编程原理相同。 进程的主线程可以在需要时创建新线程。 当线程执行完毕后,该线程自动终止; 当进程结束时,所有线程都将终止。 所有活动线程共享进程的资源。 因此,编程时需要考虑多个线程访问同一资源时的冲突问题。 当一个线程正在访问进程对象而另一个线程想要更改该对象时,可能会出现不正确的结果。 这个冲突必须在编程时解决。
2、Win32 API下的多线程编程
Win32 API是操作系统内核和应用程序之间的接口。 其作用是对内核提供的功能进行包装,应用程序通过调用相关函数来获取相应的系统功能。 为了给应用程序提供多线程功能,Win32 API函数集提供了一些用于处理多线程程序的函数集。 直接使用Win32 API进行编程有很多优点:基于Win32的应用程序执行代码少,运行效率高,但需要程序员编写更多的代码并管理系统向程序提供的所有资源。 直接使用Win32 API编写程序需要程序员对系统内核有一定的了解,这会占用程序员大量的时间来管理系统资源,从而降低程序员的工作效率。
1.使用Win32函数创建和终止线程
Win32函数库提供了操作多线程的函数,包括创建线程、终止线程、建立互斥区域等。在应用程序主线程或其他活动线程中创建新线程的函数如下:
复制代码代码如下:
(S、DWORD、NE、、DWORD、);
如果创建成功,则返回线程的句柄,否则返回NULL。 新线程创建后,该线程开始执行。 但如果在 中使用该特性,那么线程不会立即执行,而是会先挂起,等到调用完成后再启动线程。 在此过程中,可以调用以下函数来设置线程的优先级:
复制代码代码如下:
布尔(,整数);
当调用线程的函数返回时,线程自动终止。 如果需要在执行过程中终止线程,可以调用该函数:
复制代码代码如下:
无效(双字);
如果线程在线程外终止,可以调用以下函数:
复制代码代码如下:
布尔(,双字);
但需要注意的是:该函数可能会导致系统不稳定,并且线程占用的资源不会被释放。 因此,一般情况下,建议不要使用该功能。
如果要终止的线程是进程中的最后一个线程,则该线程终止后,相应的进程也应该终止。
2. 线程同步
在线程体内,如果线程完全独立,在数据访问等资源操作上不与其他线程发生冲突,则可以按照通常的单线程方法进行编程。 然而,在多线程处理中往往并非如此,线程常常需要同时访问一些资源。 由于访问共享资源引起的冲突是不可避免的,为了解决这个线程同步问题,Win32 API提供了多种同步控制对象来帮助程序员解决共享资源访问冲突。 在介绍这些同步对象之前,我们先介绍一下wait函数,因为这个函数是用来对所有控制对象进行访问控制的。
Win32 API 提供了一组等待函数,使线程能够阻止其自身的执行。 这些函数只有在其参数中的一个或多个同步对象生成信号或超过指定的等待时间后才会返回。 当等待函数没有返回时,线程就处于等待状态。 此时,线程只消耗少量的CPU时间。 使用wait函数不仅可以保证线程同步,还可以提高程序的运行效率。 最常用的等待函数是:
复制代码代码如下:
双字(,双字);
函数t可用于同时监控多个同步对象。 这个函数的声明是:
复制代码代码如下:
DWORD t(DWORD ,常量 *,BOOL ,DWORD );
(1) 互斥对象
当互斥对象不被任何线程拥有时,该对象的状态将发出信号;当它被拥有时,该对象的状态将变为无信号。 互斥对象非常适合协调多个线程对共享资源的互斥访问。 您可以按如下方式使用该对象:
首先,创建一个互斥对象并获取句柄:
复制代码代码如下:
();
然后,在线程可能发生冲突的区域之前(即访问共享资源之前)调用它,将句柄传递给函数,并请求占用互斥对象:
复制代码代码如下:
= (,5000L);
共享资源访问结束,互斥对象的占用被释放:
复制代码代码如下:
();
互斥对象同一时间只能被一个线程占用。 当互斥对象被一个线程占用时,如果另一个线程想要占用它,必须等到前一个线程释放它才能成功。
(2) 信号对象
信号对象允许同时访问多个线程共享的资源。 可以同时访问的最大线程数是在创建对象时指定的。 当线程成功申请访问时,信号对象中的计数器减一。 调用该函数后,信号对象中的计数器加一。 其中,计数器值大于等于0,但小于等于创建时指定的最大值。 如果应用程序在创建信号对象时将其计数器的初始值设置为 0,则会阻塞其他线程并保护资源。 初始化完成后,调用该函数将其计数器增加到最大值,即可进行正常访问。 您可以按如下方式使用该对象:
首先,创建信号对象:
复制代码代码如下:
();
或者打开一个信号对象:
复制代码代码如下:
();
然后,在线程访问共享资源之前调用它。
共享资源访问完成后,应释放信号对象的占用:
复制代码代码如下:
();
(3) 事件对象
事件对象(Event)是最简单的同步对象,它包括两种状态:有信号和无信号。 线程在访问资源之前,需要等待事件发生。 这时候,事件对象就最适合了。 例如:只有在通信端口缓冲区接收到数据后,才会激活监控线程。
事件对象是使用函数创建的。 该函数可以指定事件对象的类和事件的初始状态。 如果手动重置事件,它将始终保持有信号状态,直到使用函数将其重置为无信号事件。 如果是自动重置事件,则在单个等待线程被释放后,其状态将自动变为无信号状态。 用于将事件对象设置为信号状态。 创建事件时,可以为该对象命名,以便其他进程中的线程可以使用函数打开指定名称的事件对象句柄。
(4) 禁区对象
当在禁区内异步执行时,它只能在同一进程的线程之间共享资源处理。 虽然此时可以使用上面介绍的所有方法,但是使用禁区的方法使得同步管理更加高效。
使用时,首先定义一个结构体的禁区对象,并在进程中使用之前调用以下函数初始化该对象:
复制代码代码如下:
无效动作();
当线程使用禁区时,调用函数:或者ion;
当需要占用和退出禁区时,调用该函数释放占用的禁区对象,以供其他线程使用。
3.基于MFC的多线程编程
MFC是微软的VC开发集成环境中向程序员提供的基本函数库。 它以类库的形式封装了Win32 API,并以类的形式提供给开发人员。 它因其快速、简单、强大的特点而受到开发者的喜爱。 因此,建议使用MFC类库进行应用程序开发。
VC++自带的MFC类库提供了对多线程编程的支持。 基本原理与基于Win32 API的设计是一致的。 不过,由于MFC封装了同步对象,因此实现起来更加方便,并且避免了对象句柄管理的需要。 繁琐的工作。
在MFC中,线程分为两种类型:工作线程和用户界面线程。 工作线程与前面提到的线程相同。 用户界面线程是可以接收用户输入并处理事件和消息的线程。
1.工作线程
工作线程编程比较简单,设计思想与前面提到的基本相同:一个基本函数代表一个线程。 线程创建并启动后,线程进入运行状态; 如果线程使用共享资源,则需要资源同步。 这样你就可以创建一个线程并在启动线程时调用一个函数:
复制代码代码如下:
*(
,
,
整数 = AL,
单位=0,
双字=0,
S = 空);
参数为线程执行体函数,函数原型为:UINT()。
参数是传递给执行函数的参数;
参数为线程执行权限,可选值:
阿尔、圣地、东部时间。
参数是线程创建时的标志。 它可以带一个值,表示线程创建后处于挂起状态。 调用函数后线程继续运行,或者值为“0”表示线程创建后处于运行状态。
返回值是一个类对象指针,其成员变量是线程句柄。 在Win32 API模式下,线程操作的函数参数需要线程句柄,因此线程创建后,可以使用所有Win32 API函数来执行->线程相关的操作。
注意:如果在类对象中创建并启动线程,则线程函数应定义为类外部的全局函数。
2.用户界面线程
基于 MFC 的应用程序有一个应用程序对象,它是派生类的对象。 该对象代表应用程序进程的主线程。 当线程执行完毕并退出线程时,进程自动结束,因为进程中不存在其他线程。 该类派生自用户界面线程,并且是用户界面线程的基类。 当我们编写用户界面线程时,我们需要派生自己的线程类,它可以帮助我们完成这项工作。
首先派生一个新类并将基类设置为 。 注意:类和宏是必需的,因为创建线程时需要动态创建类的对象。 初始化和终止代码可以根据需要分别放在类和函数中。 如果需要创建窗口,可以在函数中完成。 然后创建线程并启动线程。 有两种方法可以创建用户界面线程。 MFC 提供了两个版本的函数,其中之一用于创建用户界面线程。 第二种方法分为两步:首先调用线程类的构造函数创建线程对象; 其次,调用::函数创建线程。 线程建立并启动后,在线程函数执行期间始终有效。 如果是线程对象,则在删除该对象之前线程会终止。 线程结束工作已经为我们完成。
3. 线程同步
前面我们介绍了Win32 API提供的几个与线程同步相关的对象。 这些对象都封装在MFC类库中。 它们有共同的基类,对应关系为:对应、Mutex对应、Event对应、对应。 另外,MFC还封装了两个等待函数,即and。 由于四个对象的用法类似,这里我们举个例子来说明:
创建一个对象:
复制代码代码如下:
互斥体(FALSE,NULL,NULL);
或者
复制代码代码如下:
互斥体;
当每个线程想要访问共享资源时,使用以下代码:
复制代码代码如下:
sl(&互斥体);
sl.Lock();
如果(sl。())
//操作共享资源...
sl.();
4。结论
如果用户的应用程序需要同时处理多个任务,那么使用多线程是一个理想的选择。 这里提醒大家,多线程编程时,要特别注意处理好资源共享问题和多线程调试问题。
希望这篇文章对大家的VC编程有所帮助。