Windows平台下的多线程编程

简介:     线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件、信号标识及动态分配的内存等。一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度程序控制,调度程序决定哪个线程可执行以及什么时候执行线程。


 

 

线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件、信号标识及动态分配的内存等。一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度程序控制,调度程序决定哪个线程可执行以及什么时候执行线程。线程有优先级别,优先权较低的线程必须等到优先权较高的线程执行完后再执行。在多处理器的机器上,调度程序可将多个线程放到不同的处理器上去运行,这样可使处理器任务平衡,并提高系统的运行效率。 

Windows是一种多任务的操作系统,在Windows的一个进程内包含一个或多个线程。32Windows环境下的Win32 API提供了多线程应用程序开发所需要的接口函数,而利用VC中提供的标准C库也可以开发多线程应用程序,相应的MFC类库封装了多线程编程的类,用户在开发时可根据应用程序的需要和特点选择相应的工具。为了使大家能全面地了解Windows多线程编程技术,本文将重点介绍Win32 APIMFC两种方式下如何编制多线程程序。 

多线程编程在Win32方式下和MFC类库支持下的原理是一致的,进程的主线程在任何需要的时候都可以创建新的线程。当线程执行完后,自动终止线程当进程结束后,所有的线程都终止。所有活动的线程共享进程的资源,因此,在编程时需要考虑在多个线程访问同一资源时产生冲突的问题。当一个线程正在访问某进程对象,而另一个线程要改变该对象,就可能会产生错误的结果,编程时要解决这个冲突。 


Win32 API下的多线程编程 


Win32 APIWindows操作系统内核与应用程序之间的界面,它将内核提供的功能进行函数包装,应用程序通过调用相关函数而获得相应的系统功能。为了向应用程序提供多线程功能,Win32 API函数集中提供了一些处理多线程程序的函数集。直接用Win32 API进行程序设计具有很多优点基于Win32的应用程序执行代码小,运行效率高,但是它要求程序员编写的代码较多,且需要管理所有系统提供给程序的资源。用Win32 API直接编写程序要求程序员对Windows系统内核有一定的了解,会占用程序员很多时间对系统资源进行管理,因而程序员的工作效率降低。 


1. Win32函数创建和终止线程 


Win32函数库中提供了操作多线程的函数,包括创建线程、终止线程、建立互斥区等。在应用程序的主线程或者其他活动线程中创建新的线程的函数如下: 

HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId); 



如果创建成功则返回线程的句柄,否则返回 NULL 。创建了新的线程后,该线程就开始启动执行了。但如果在 dwCreationFlags 中使用了 CREATE_SUSPENDED 特性,那么线程并不马上执行,而是先挂起,等到调用 ResumeThread 后才开始启动线程,在这个过程中可以调用下面这个函数来设置线程的优先权: 

BOOL SetThreadPriority(HANDLE hThread,int nPriority); 

当调用线程的函数返回后,线程自动终止。如果需要在线程的执行过程中终止则可调用函数: 

VOID ExitThread(DWORD dwExitCode); 

如果在线程的外面终止线程,则可调用下面的函数: 

BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode); 

但应注意 该函数可能会引起系统不稳定,而且线程所占用的资源也不释放。因此,一般情况下,建议不要使用该函数。 

如果要终止的线程是进程内的最后一个线程,则线程被终止后相应的进程也应终止。 

2. 线程的同步 


在线程体内,如果该线程完全独立,与其他线程没有数据存取等资源操作上的冲突,则可按照通常单线程的方法进行编程。但是,在多线程处理时情况常常不是这样,线程之间经常要同时访问一些资源。由于对共享资源进行访问引起冲突是不可避免的,为了解决这种线程同步问题, Win32 API 提供了多种同步控制对象来帮助程序员解决共享资源访问冲突。在介绍这些同步对象之前先介绍一下等待函数,因为所有控制对象的访问控制都要用到这个函数。 

Win32 API 提供了一组能使线程阻塞其自身执行的等待函数。这些函数在其参数中的一个或多个同步对象产生了信号,或者超过规定的等待时间才会返回。在等待函数未返回时,线程处于等待状态,此时线程只消耗很少的 CPU 时间。使用等待函数既可以保证线程的同步,又可以提高程序的运行效率。最常用的等待函数是: 

DWORD WaitForSingleObject(HANDLE hHandle DWORD dwMilliseconds); 

而函数 WaitForMultipleObject 可以用来同时监测多个同步对象,该函数的声明为: 

DWORD WaitForMultipleObject(DWORD nCount,CONST HANDLE *lpHandles,BOOL bWaitAll,DWORD dwMilliseconds); 

1 )互斥体对象 

Mutex 对象的状态在它不被任何线程拥有时才有信号,而当它被拥有时则无信号。 Mutex 对象很适合用来协调多个线程对共享资源的互斥访问。可按下列步骤使用该对象: 

首先,建立互斥体对象,得到句柄: 

HANDLE CreateMutex(); 

然后,在线程可能产生冲突的区域前(即访问共享资源之前)调用 WaitForSingleObject ,将句柄传给函数,请求占用互斥对象: 

dwWaitResult = WaitForSingleObject(hMutex,5000L); 

共享资源访问结束,释放对互斥体对象的占用: 

ReleaseMutex(hMutex); 

互斥体对象在同一时刻只能被一个线程占用,当互斥体对象被一个线程占用时,若有另一线程想占用它,则必须等到前一线程释放后才能成功。 

2 )信号对象 

信号对象允许同时对多个线程共享资源进行访问,在创建对象时指定最大可同时访问的线程数。当一个线程申请访问成功后,信号对象中的计数器减一,调用 ReleaseSemaphore 函数后,信号对象中的计数器加一。其中,计数器值大于或等于0,但小于或等于创建时指定的最大值。如果一个应用在创建一个信号对象时,将其计数器的初始值设为0,就阻塞了其他线程,保护了资源。等初始化完成后,调用 ReleaseSemaphore 函数将其计数器增加至最大值,则可进行正常的存取访问。可按下列步骤使用该对象: 

首先,创建信号对象: 

HANDLE CreateSemaphore(); 

或者打开一个信号对象: 

HANDLE OpenSemaphore(); 

然后,在线程访问共享资源之前调用 WaitForSingleObject 。 

共享资源访问完成后,应释放对信号对象的占用: 

ReleaseSemaphore(); 

3 )事件对象 

事件对象 (Event) 是最简单的同步对象,它包括有信号和无信号两种状态。在线程访问某一资源之前,需要等待某一事件的发生,这时用事件对象最合适。例如:只有在通信端口缓冲区收到数据后,监视线程才被激活。 

事件对象是用 CreateEvent 函数建立的。该函数可以指定事件对象的类和事件的初始状态。如果是手工重置事件,那么它总是保持有信号状钡接肦 esetEvent 函数重置成无信号的事件。如果是自动重置事件,那么它的状态在单个等待线程释放后会自动变为无信号的。用 SetEvent 可以把事件对象设置成有信号状态。在建立事件时,可以为对象命名,这样其他进程中的线程可以用 OpenEvent 函数打开指定名字的事件对象句柄。 

4 )排斥区对象 

在排斥区中异步执行时,它只能在同一进程的线程之间共享资源处理。虽然此时上面介绍的几种方法均可使用,但是,使用排斥区的方法则使同步管理的效率更高。 

使用时先定义一个 CRITICAL_SECTION 结构的排斥区对象,在进程使用之前调用如下函数对对象进行初始化

VOID InitializeCriticalSection(LPCRITICAL_SECTION); 

当一个线程使用排斥区时,调用函数: EnterCriticalSection 或者 TryEnterCriticalSection; 

当要求占用、退出排斥区时,调用函数 LeaveCriticalSection ,释放对排斥区对象的占用,供其他线程使用。 

基于MFC的多线程编程 


MFC 是微软的 VC 开发集成环境中提供给程序员的基础函数库,它用类库的方式将 Win32 API 进行封装,以类的方式提供给开发者。由于其快速、简捷、功能强大等特点深受广大开发者喜爱。因此,建议使用 MFC 类库进行应用程序的开发。 

VC++ 附带的 MFC 类库中,提供了对多线程编程的支持,基本原理与基于 Win32 API 的设计一致,但由于 MFC 对同步对象做了封装,因此实现起来更加方便,避免了对象句柄管理上的烦琐工作。 

MFC 中,线程分为两种:工作线程和用户接口线程。工作线程与前面所述的线程一致,用户接口线程是一种能够接收用户的输入、处理事件和消息的线程。 

1. 工作线程 


工作线程编程较为简单,设计思路与前面所讲的基本一致 一个基本函数代表了一个线程,创建并启动线程后,线程进入运行状态 如果线程用到共享资源,则需要进行资源同步处理。这种方式创建线程并启动线程时可调用函数: 

CWinThread*AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam,int nPriority= THREAD_PRIORITY_NORMAL,UINT nStackSize =0,DWORD dwCreateFlags=0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);


参数 pfnThreadProc 是线程执行体函数,函数原形为 : UINT ThreadFunction( LPVOID pParam) 。 

参数 pParam 是传递给执行函数的参数; 

参数 nPriority 是线程执行权限,可选值: 

THREAD_PRIORITY_NORMAL THREAD_PRIORITY_LOWEST THREAD_PRIORITY_HIGHEST THREAD_PRIORITY_IDLE 。 

参数 dwCreateFlags 是线程创建时的标志,可取值 CREATE_SUSPENDED ,表示线程创建后处于挂起状态,调用 ResumeThread 函数后线程继续运行,或者取值 “0” 表示线程创建后处于运行状态。 

返回值是 CWinThread 类对象指针,它的成员变量 m_hThread 为线程句柄,在 Win32 API 方式下对线程操作的函数参数都要求提供线程的句柄,所以当线程创建后可以使用所有 Win32 API 函数对 pWinThread->m_Thread 线程进行相关操作。 

注意:如果在一个类对象中创建和启动线程时,应将线程函数定义成类外的全局函数。 

2. 用户接口线程 


基于 MFC 的应用程序有一个应用对象,它是 CWinApp 派生类的对象,该对象代表了应用进程的主线程。当线程执行完并退出线程时,由于进程中没有其他线程存在,进程自动结束。类 C inApp C inThread 派生出来, C inThread 是用户接口线程的基本类。我们在编写用户接口线程时,需要从 C inThread 派生我们自己的线程类, ClassWizard 可以帮助我们完成这个工作。 

先用 ClassWizard 派生一个新的类,设置基类为 CwinThread 。注意:类的 DECLARE_DYNCREATE IMPLEMENT_DYNCREATE 宏是必需的,因为创建线程时需要动态创建类的对象。根据需要可将初始化和结束代码分别放在类的 InitInstance ExitInstance 函数中。如果需要创建窗口,则可在 InitInstance 函数中完成。然后创建线程并启动线程。可以用两种方法来创建用户接口线程, MFC 提供了两个版本的 AfxBeginThread 函数,其中一个用于创建用户接口线程。第二种方法分为两步进行:首先,调用线程类的构造函数创建一个线程对象;其次,调用 CWinThread::CreateThread 函数来创建该线程。线程建立并启动后,在线程函数执行过程中一直有效。如果是线程对象,则在对象删除之前,先结束线程。 CWinThread 已经为我们完成了线程结束的工作。 


3. 线程同步 


前面我们介绍了 Win32 API 提供的几种有关线程同步的对象,在 MFC 类库中对这几个对象进行了类封装,它们有一个共同的基类 CSyncObject ,它们的对应关系为 : Semaphore 对应 CSemaphore Mutex 对应 CMutex Event 对应 CEvent CriticalSection 对应 CCriticalSection 。另外, MFC 对两个等待函数也进行了封装,即 CSingleLock CMultiLock 。因四个对象用法相似,在这里就以 CMutex 为例进行说明: 

创建一个 CMutex 对象

CMutex mutex(FALSE,NULL,NULL); 

CMutex mutex; 

当各线程要访问共享资源时使用下面代码: 

CSingleLock sl(&mutex); 

sl.Lock(); 

if(sl.IsLocked()) 

// 对共享资源进行操作 ... 

sl.Unlock(); 


结束语 


如果用户的应用程序需要多个任务同时进行相应的处理,则使用多线程是较理想的选择。这里,提醒大家注意的是在多线程编程时要特别小心处理资源共享问题以及多线程调试问题

相关文章
|
1天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
4天前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
3天前
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
8天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
17天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
14天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
17天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
25天前
|
安全 程序员 API
|
18天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
45 1
|
21天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
下一篇
无影云桌面