首页 [计算机软件及应用]2012毕设论文——基于VC的多线程通信程序设计

[计算机软件及应用]2012毕设论文——基于VC的多线程通信程序设计

举报
开通vip

[计算机软件及应用]2012毕设论文——基于VC的多线程通信程序设计[计算机软件及应用]2012毕设论文——基于VC的多线程通信程序设计 基于VC的多线程通信程序设计 专业: 学号: 学生姓名: 指导教师: 摘 要 在多任务Windows编程中,为了提高代码的时间、空间效率,广泛采取后 恰当地完成任务,往往以线程来实现前台任务及后台任台程序和前台程序。为了 务。 线程是操作系统的调度单元,也是进程的一条执行路径。它包含独立的堆栈和CPU寄存器状态,进程内的每个线程共享该进程的所有资源,包括打开的文件、信号标识及动态分配的内存等。一个进程可以拥有一个或多个线程,它从主线程...

[计算机软件及应用]2012毕设论文——基于VC的多线程通信程序设计
[计算机软件及应用]2012毕设论文——基于VC的多线程通信程序设计 基于VC的多线程通信程序设计 专业: 学号: 学生姓名: 指导教师: 摘 要 在多任务Windows编程中,为了提高代码的时间、空间效率,广泛采取后 恰当地完成任务,往往以线程来实现前台任务及后台任台程序和前台程序。为了 务。 线程是操作系统的调度单元,也是进程的一条执行路径。它包含独立的堆栈和CPU寄存器状态,进程内的每个线程共享该进程的所有资源,包括打开的文件、信号标识及动态分配的内存等。一个进程可以拥有一个或多个线程,它从主线程的执行开始进而创建一个或多个附加线程, 每个线程完成一个特定的任务, 多个线程并行地运行在同一进程中, 最终完成多个任务。 在Visual C++6.0编程环境中,有两种多线程编程方法:一种是使用C运行时库及Win32 API函数编写C风格的Win32应用程序,另一种方法是使用MFC基本类库编写C++风格的应用程序。前者执行代码小巧,运行效率高,但要求程序员编写的代码较多,且需要将近掌握近千个Windows函数,使用不方便。后者借助于其应用程序向导的帮助,以其封装好的类、构造函数、响应函数及应用程序框架和许多控件等的帮助,可以快速建立起应用程序。并且,借助于该开发环境及类代码的高可靠性、 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 化、可获得高质量的应用程序代码。 本文以用Visual C++6.0提供的 MFC类库实现多线程编程为重点,详细介绍了其实现原理,方法和相关技术。 关键词:VC;多线程;用户界面线程;工作者线程;多线程通信 I Programming of Multi-threading Communication Based on VC Abstract In order to improve the code’s efficiency both in executing time and storing space, downstage and backstage programming were widely used on Multi-task Windows programs. To complete a task properly ,threads were used to realize both downstage and backstage duties commonly. A thread is a scheduling unit of operating system, also is a path of process execution.It contains separate stack and the CPU register states, and each thread in the shares all resources of the process, including open file, signal identification and dynamic allocation of memory and so on. A process can have one or more threads, it starts from the execution of the main thread and then creates one or more additional threads, each thread to perform a specific task, multiple threads to run in parallel in the same process, finally complete a number of tasks. In Visual C++6.0 programming environment, there are two Multi-threading programming methods .One is programming Win32 application of C Run-time Library style by C and Win32 API function. The other is programming Win32 application of C++ style by MFC ( Microsoft Foundation Class Library ).The former code execution is small and high efficiency, but it requires programmers to write code more, and programmers need to grasp nearly one thousand Windows function , therefore it’s inconvenient to use. The latter by the help of its application wizard, with its packaging good category, constructor, response function and application framework and many controls’ help, can quickly build up applications. And by the means of the development environment and class code’s high reliability, standard , we can get application code of high quality. This paper takes using the Visual C++6.0 MFC class library to achieve multi-threaded programming as the focus, and introduces the implement principle, method and related technology in detail. Keyword: VC; Multi-threading;User Interface Thread;Worker Thread; Multi-threading Communication II 目 录 摘 要 ..................................................................................................................... I Abstract ..................................................................................................................... II 第一章 绪论 ........................................................................................................... 1 1. 1 课题的研究背景 ........................................................................................ 1 1. 2 多线程概述 ................................................................................................ 2 1. 3 多线程的基本应用..................................................................................... 2 1. 4 本文的主要研究内容 ................................................................................. 3 1. 5 Visual C++6.0简介 ..................................................................................... 3 第二章 如何在VC中的实现多线程编程 .............................................................. 4 2. 1 VC的两种多线程编程方法 ........................................................................ 4 2. 1. 1 基于Win32 API的多线程编程 ....................................................... 4 2. 1. 2 基于MFC的多线程编程 ................................................................ 7 2. 2 多线程通信技术 ........................................................................................ 9 2. 2. 1 使用全局变量进行通信 .................................................................. 9 2. 2. 2 使用用户定义的消息通信 .............................................................10 2. 2. 3 使用事件对象实现通信 .................................................................10 第三章 多线程通信程序设计实例 ....................................................................... 11 3. 1 程序功能设计 ........................................................................................... 11 3. 2 设计思路 ................................................................................................... 11 3. 3 实现步骤 ...................................................................................................12 3. 3. 1 创建MFC工程 ..............................................................................12 3. 3. 2 编辑对话框资源 .............................................................................15 3. 3. 3 编写程序实现代码 .........................................................................17 3. 4 程序运行结果 ...........................................................................................27 3. 4. 1 窗口的创建.....................................................................................28 3. 4. 2 窗口的通信.....................................................................................29 结论 .........................................................................................................................33 参考文献(References) .........................................................................................34 致谢 .........................................................................................................................35 1 第一章 绪论 1. 1 课题的研究背景 多线程是现代操作系统( 如Windows 95、Windows NT) 中出现的概念。传统的操作系统中,资源分配和CPU调度的单位是进程,即一个程序的一次执行。进程在任何时候只有一个执行现场,即单线程结构。但随着计算机技术的高速发展,这种单线程结构的进程已不能很好的适应计算机的发展。 首先,计算机硬件向多处理机,网络方面发展,这就要求操作系统能适应这种发展,合理有效的使用各处处理机和网络上的其他处理机,许多任务可以分配到不同的处理机上同时运行。这是传统的单线程结构进程所不能有效实现的。 其次,应用程序要求并发执行。如数据库中,可以同时有多个用户在交互执行,同时对几个文件或网络操作。又如窗口系统中,同时处理多个子窗口的请求等,这就要求操作系统提供一些机制,以满足用户这种需求。在一个程序中设计出多个线程同时执行,而这又不是多个进程组合在一起能完成的,因为他们之间共享大部分资源。 基于上述原因,操作系统在系统结构上有了新的发展,与传统操作系统中的单线程结构相对应,提出了多线程结构的概念。很多著名的操作系统都已采用了多线程结构,如:Solaris 2.x ,Mach 2.6 ,OS/2, Linux ,Chorus等,当然,也包括Window s95和Windows/NT。 在操作系统中引入线程带来的主要好处有: 1. 在进程内创建、终止线程比创建、终止进程要快; 2. 同一进程内的线程间切换比进程间的切换要快,尤其是用户级线程间的切换。另外,线程的出现还因为以下几个原因: < 1 > 并发程序的并发执行,在多处理环境下更为有效。一个并发程序可以建立一个进程,而这个并发程序中的若干并发程序段就可以分别建立若干线程,使这些线程在不同的处理机上同时执行。 < 2 >每个进程具有独立的地址空间,而该进程内的所有线程共享该地址空间。这样可以解决父子进程模型中,子进程必须复制父进程地址空间的问题。 < 3 >线程对解决客户/服务器模型非常有效。 1 1. 2 多线程概述 进程和线程都是操作系统的概念。在操作系统中,进程被定义为应用程序的运行实例,是应用程序的一次动态执行。线程是进程内部程序执行的路径,是进程的一个执行单元。从根本上说,线程是可由系统调度的一个最简单的代码单元,负责执行包含在进程地址空间中的程序代码。 一个进程可以拥有一个或多个线程,进程内的每个线程共享该进程的所有资源,包括打开的文件、信号标识及动态分配的内存等。系统创建好进程后,实际上就启动执行了该进程的主执行线程。该主执行线程无需由用户去主动创建,而 形式,将程序的启动点提供给Windows是由系统自动创建的。它以函数地址的 系统。随后,用户根据需要在进程中创建一个或多个附加线程, 每个线程完成一个特定的任务,多个线程并发地运行于同一个进程中。当进程退出时,该进程所产生的线程都会被强制退出并清除。 在运行一个多线程的程序时,从表面上看,这些线程似乎在同时运行。而实际情况并非如此,目前大多数的计算机都是单处理器(CPU)的,为了运行所有这些线程,操作系统为每个独立线程安排一些CPU时间,操作系统以轮换方式向线程提供时间片,线程在自己的时间内运行,因时间片相当短,因此,给用户的感觉,就好像这些线程都在同时运行。只有在多核CPU或多CPU的计算机上,多个线程才能够真正地同时运行。 1. 3 多线程的基本应用 目前多线程技术在应用软件和系统软件的实现中得到了广泛的使用,如操作系统中的并行文件操作;数据库中的多用户事务处理;窗口系统中的多个相关子窗口操作;实时系统中多事件的同时响应;网络中多个客户共享网络服务器等等. 下面给出了一些线程应用的例子: ( 1 ) 加快执行速度. 一个多线程进程在计算一批数据的同时,读入设备(网络、终端、打印机、硬盘)上的下一批数据,而这分别由两个线程实现. ( 2 ) C,S 应用. 局域网上服务器处理多个用户文件(任务)请求时,创建多个线程,若该服务器是多CPU的,则同一进程中的多线程可以同时运行在不同CPU 上. ( 3 ) 前台和后台工作. 如在一个电子表格软件中,一个线程执行显示菜单和读入用户输入,同时,另一个线程执行用户命令和修改电子表格. 2 ( 4 ) 多事件处理. 每当用户要求执行一个动作时,就建立一个独立线程来完成这项动作. 当用户要求有多个动作时,就由多个线程来实现. ( 5 ) 多窗口系统. 当用户要求建立多窗口系统时,用多个线程分别实现主 [1]从窗口. 1. 4 本文的主要研究内容 1. 详细介绍了多线程的基本概念,应用领域以及其实现原理。 2. Visual C++6.0对多线程编程的支持。 3. 线程的控制(主要实现线程的创建、终止、挂起和恢复等操作)。 4. 线程的优先级管理,优先权较低的线程必须等到优先权较高的线程执行完 后再执行。 5. 线程的通信,包括三种实现方法:使用全局变量、使用用户自定义消息、 使用事件对象。 6. 给出多线程通信程序设计的实例。 7. 得出结论。 1. 5 Visual C++6.0简介 Visual C++6.0简称VC或者VC6.0,是微软公司推出的开发Win32环境程序,面向对象的可视化集成编程系统,具有集成开发环境,可提供编辑C语言,C++以及C++/CLI等编程语言。它由许多组件组成,包括编辑器、调试器以及程序向导AppWizard、类向导Class Wizard等开发工具。 这些组件通过一个名为Developer Studio的组件集成为和谐的开发环境。 Visual C++6.0不但具有程序框架自动生成、灵活方便的类管理、代码编写和界面设计集成交互操作、可开发多种程序等优点,而且通过简单的设置就可使其生成的程序框架支持数据库接口、OLE2,WinSock网络、3D控制界面。 在Visual C++6.0开发环境中,应用程序的开发主要有两种模式,一种是Win32 API方式,另一种则是MFC方式。API是应用程序编程接口的缩写,它为应用程序提供系统的各种特殊函数及数据结构定义,Windows应用程序可以利用上千个标准API函数调用系统功能。而MFC则是Visual C++ 6.0集成开发环境中提供给用户的基础函数库,它用类库的方式将Win32 API进行封装,以类的方式提供给开发者。传统的Win32 API开发方式比较繁琐,而MFC则是对Win32 API再次封装,所以MFC相对于Win32 API开发更具备效率优势。 3 第二章 如何在VC中的实现多线程编程 2. 1 VC的两种多线程编程方法 在Visual C++6.0编程环境中,有两种多线程编程方法:一种是使用C运行时库及Win32 API函数编写C风格的32位Win32应用程序,另一种方法是使用MFC类库编写C++风格的应用程序,二者各有其优缺点。前者执行代码小巧,运行效率高,但要求程序员编写的代码较多,且需要将近掌握近千个Windows函数,使用不方便。后者借助于其应用程序向导的帮助,以其封装好的类、构造函数、响应函数及应用程序框架和许多控件等的帮助,可以快速建立起应用程序,大大简化了程序开发的过程并减少了开发的工作量和难度,但其缺点是类库代码很庞大。 2. 1. 1 基于Win32 API的多线程编程 Win32 API 是Windows 操作系统内核与应用程序之间的界面, 它把内核提供的功能包装成函数, 其中也包括一些处理多线程的函数集。Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终止以及通信等工作。 ( 1 ) 线程的创建: HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); 其中各参数的含义如下: lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构决 定了线程的安全属性,一般置为 NULL; dwStackSize:指定了线程的堆栈深度,一般都设置为0; lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。 一般情况为 (LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是线程函数名; lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数; 4 dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0, 线程在被创建后就会立即开始执行;如果该参数为 CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并 不马上执行,直至函数 ResumeThread被调用; lpThreadId:该参数返回所创建线程的ID; 如果创建成功则返回线程的句柄,否则返回 NULL。 该函数在其调用进程的进程空间里创建一个新的线程,如果创建成功则返回已建线程的句柄,否则返回NULL。 ( 2 ) 线程的挂起与恢复: DWORD SuspendThread(HANDLE hThread); 的句柄。该函数用于挂起指定的线程,其中参数hThread为所要挂起的线程 如果函数执行成功,则线程的执行被终止。 DWORD ResumeThread(HANDLE hThread); 该函数用于结束线程的挂起状态,恢复线程的执行,其中参数hThread为所要恢复的线程的句柄。 ( 3 ) 线程的终止: a. 线程的自身终止: VOID ExitThread(DWORD dwExitCode); 该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。 b. 应用程序的强行终止: BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode); 一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序也可以调用TerminateThread强行终止某一线程的执行。 注意,使用TerminateThread( )终止某个线程的执行是不安全的,可能会引起系统不稳定;虽然该函数立即终止线程的执行,但并不释放线程所占用的资源。因此,一般不 建议 关于小区增设电动车充电建议给教师的建议PDF智慧城市建议书pdf给教师的36条建议下载税则修订调整建议表下载 使用该函数。 ( 4 ) 线程的通信: BOOL PostThreadMessage(DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam); 5 其中各参数的含义如下: idThread:将接收消息的线程的ID; Msg:指定用来发送的消息; wParam:同消息有关的字参数; lParam:同消息有关的长参数; 该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。 调用该函数时,如果即将接收消息的线程没有创建消息循环,则该函数执行失败。 ( 5 ) 线程的睡眠: VOID Sleep(DWORD dwMilliseconds); 该函数可使线程暂停自己的运行,直到dwMilliseconds毫秒过去为止。它告诉系统,自身不想在某个时间段内被调度。 ( 6 ) 线程的优先级 不同的线程具有不同的优先级,而优先级决定了线程能够得到多少CPU时间。高优先级的线程通常会比一般优先级的线程得到更多的CPU时间,如果程序中存在不止一个高优先级的线程,操作系统将在这些线程之间循环分配CPU时间。 当一个线程被首次创建时,就会有一个默认的优先级,它的优先级等同于它所属进程的优先级。一个线程的优先级是相对于其所属进程的优先级而言的。线程与包含它的进程的优先级关系如下: 线程优先级 = 进程类基本优先级 + 线程相对优先级 进程类的基本优先级包括: 1. 实时:REALTIME_PRIORITY_CLASS; 2. 高:HIGH _PRIORITY_CLASS; 3. 高于正常:ABOVE_NORMAL_PRIORITY_CLASS; 4. 正常:NORMAL _PRIORITY_CLASS; 5. 低于正常:BELOW_ NORMAL _PRIORITY_CLASS; 6. 空闲:IDLE_PRIORITY_CLASS。 6 线程的相对优先级包括: 1. 空闲:THREAD_PRIORITY_IDLE; 2. 最低线程:THREAD_PRIORITY_LOWEST; 3. 低于正常线程:THREAD_PRIORITY_BELOW_NORMAL; 4. 正常线程:THREAD_PRIORITY_ NORMAL (缺省); 5. 高于正常线程:THREAD_PRIORITY_ABOVE_NORMAL; 6. 最高线程:THREAD_PRIORITY_HIGHEST; 7. 关键时间:THREAD_PRIOTITY_CRITICAL。 在单个进程内可以通过调用SetThreadPriority函数改变线程的相对优先级, 调用GetThreadPriority函数获取线程当前的相对优先级。 ? BOOL SetThreadPriority(HANDLE hThread, int nPriority); 其中参数hThread是指向待修改优先级线程的句柄, ? Int GetThreadPriority (HANDLE hThread); 如果函数执行发生错误,会返回THREAD_PRIORITY_ERROR_RETURN标志。如果函数成功地执行,会返回优先级标志。 2. 1. 2 基于MFC的多线程编程 MFC (Microsoft 基本类库) 是Visual C++ 6.0集成开发环境中提供给用户的基础函数库,它用类库的方式将Win32 API进行封装,以类的方式提供给开发者。它有快速、简捷、功能强大等特点。在MFC 类库中,提供了对多线程编程的支持,基本原理与基于Win32 API 的设计一致。 [2]在VC + + 6. 0 下,MFC应用程序的线程由CWinThread对象表示。在MFC中,线程分为两种:用户界面 线程 和工作线程。用户界面线程常用于接收用户的输入,处理相应你给的事件和消息。在用户界面线程中,包含一个消息循环,其中CwinApp就是一个典型的例子,它从CwinThread派生出来,负责处理用户输入产生的时间和消息。工作线程常用于任务处理(如计算),不要求用户输入, [3]对用户而言,它在后台运行。 ( 1 ) 线程的创建与启动 程序一般不需要直接创建CWinThread对象,通过调用AfxBeginThread ()函 7 [2]数就会自动创建一个CWinThread对象 。该函数有两种重载形式,分别用于 创建工作者线程和用户界面线程。两种重载函数原型和参数分别说明如下: 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 ExecutingFunction(LPVOID pParam); 注意,ExecutingFunction()应返回一个UINT类型的值,用以指明该函 明执行成功。 数结束的原因。一般情况下,返回0表 pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。 它可以是数值,或是指向一个结构的指针,甚至可以被忽略; nPriority:线程的优先级。如果为0,则线程与其父线程具有相同的优先级; nStackSize:线程为自己分配堆栈的大小,其单位为字节。如果 nStackSize被设 为0,则线程的堆栈被设置成与父线程堆栈相同大小; dwCreateFlags:如果为0,则线程在创建后立刻开始执行。如果为 CREATE_SUSPEND,则线程在创建后立刻被挂起; lpSecurityAttrs:线程的安全属性指针,一般为 NULL; 2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass, int nPriority=THREAD_PRIORITY_NORMAL, UINT nStackSize=0, DWORD dwCreateFlags=0, LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL); pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针, 该导出类定义了被创建的用户界面线程的启动、退出等;其它参数的意义同1) 式。使用函数的这个原型生成的线程也有消息机制,在以后的例子中我们将发现 同主线程的机制几乎一样。 ( 2 ) 线程的挂起和恢复 8 CWinThread类中包含了应用程序悬挂和恢复它所创建的线程的函数,其中SuspendThread( ) 用来挂起指定的线程,暂停线程的执行;ResumeThread( ) 用来恢复指定线程的执行。注意,如果对一个线程连续若干次执行SuspendThread( ),则需要连续执行相应次的ResumeThread( ) 来恢复线程的运行。 ( 3 ) 线程的终止 终止线程有三种途径,线程可以在自身内部调用AfxEndThread()来终止自身的运行;可以在线程的外部调用BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)来强行终止一个线程的运行,然后调用CloseHandle( )函数释放线程所占用的堆栈;第三种方法是改变全局变量,使线程的执行函数返回,则该线程终止。 ( 4 ) 线程的优先级管理 以下的CwinThread类的成员函数用于线程优先级的操作: int GetThreadPriority( ); BOOL SetThreadPriority(int nPriority); 上述的二个函数分别用来获取和设置线程的优先级,这里的优先级,是相对于该线程所处的优先权层次而言的,处于同一优先权层次的线程,优先级高的线程先运行;处于不同优先权层次上的线程,谁的优先权层次高,谁先运行。 2. 2 多线程通信技术 通常情况下,应用程序中的一个次级线程总是要为主线程完成某种特定的任务,这就隐含着表示在主线程和次级线程之间需要建立一个通信的通道,也就是主线程和次级线程间要进行通信。这种线程间的通信不但是难以避免的,而且在多线程编程中也是复杂和频繁的。一般情况下,有下面的几种方法实现这种通信任务:使用全局变量、使用用户自定义消息、使用事件对象。 2. 2. 1 使用全局变量进行通信 我们知道,属于同一个进程的各个线程共享操作系统分配给该进程的所有资源,因此,实现线程间通信最简单的一种方法就是使用全局变量(静态变量也可以)。该方法将全局变量作为线程监视的对象,并通过在主线程对此变量值的改变而实现对子线程的控制。由于这里的全局变量需要在使用它的线程之外对其值 9 进行改变,这就需要通过volatile关键字对此变量进行说明,来告诉编译器这个 全局变量是易变的,让编译器不要对这个变量进行优化,即无需将它放到一个寄存器中,并且该值可被外部改变。如果线程间所需传递的信息较复杂,我们可以定义一个结构,通过传递指向该结构的指针进行传递信息。 2. 2. 2 使用用户定义的消息通信 全局变量在线程通信中的应用多用在主线程对子线程的控制上,而从子线程向主线程的信息反馈则多采用自定义消息的方式来进行。在Windows程序设计 中,应用程序的每一个线程都拥有自己的消息队列,这样一来,线程之间利用消息来传递信息就变的非常简单。首先用户要定义一个用户消息,如下所示: #define WM_USERMSG WMUSER+100 然后在需要的时候,在一个线程中调用 ::PostMessage((HWND)param,WM_USERMSG,0,0) 或 CwinThread::PostThreadMessage(WM_USERMSG,0,0) 来向另外一个线程发送这个消息,上述函数的四个参数分别是消息将要发送到的目的窗口的句柄、要发送的消息标志符、消息的参数WPARAM和LPARAM。 2. 2. 3 使用事件对象实现通信 在线程之间传递信号进行通信比较复杂的方法是利用事件(Event)内核对象,主要通过对事件对象的监视来实现线程间的通信。事件对象可以用MFC的 CEvent类的对象来表示,它包括有信号和无信号两种状态,分别由CEvent类的 成员函数SetEvent( )和ResetEvent( )来产生。线程可以通过WaitForSingleObject( )或WaitForMultipleObjects( )之类的通知等待函数监视处于有信号状态的事件,以 WaitForSingleObject( )的函数原型为: 便在适当的时候执行对事件的操作。 DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); 函数将在hHandle对象有信号时或是在等待时间超出由dwMilliseconds设定 的超时时间间隔返回。其返回值可以为WAIT_ABANDONED、WAIT_OBJECT_0和WAIT_TIMEOUT,分别表示被等待的互斥量(Mutex)对象没有被释放、等待的对象信号置位和超时。通过对返回值的判断可以区分出引起WaitForSingleObject()函数返回的原因。 10 第三章 多线程通信程序设计实例 在Visual C++6.0开发环境中,应用程序的开发主要有两种模式,一种是Win32 API方式,另一种则是MFC方式。API是应用程序编程接口的缩写,它为应用程序提供系统的各种特殊函数及数据结构定义,Windows应用程序可以利用上千个标准API函数调用系统功能。而MFC则是Visual C++ 6.0集成开发环境中提供给用户的基础函数库,它用类库的方式将Win32 API进行封装,以类的方式提供给开发者。传统的Win32 API开发方式比较繁琐,而MFC则是对Win32 API再次封装,所以MFC相对于Win32 API开发更具备效率优势。因此,本文直接使用Visual C++6.0 MFC开发多线程通信程序。 3. 1 程序功能设计 本实例是一个多线程程序,要求实现多线程相互通信。为了直观的看到线程的通信行为,本实例用多个窗口来进行线程的通信演示,一个窗口对应一个线程。并且,每个窗口各自都有一个用来显示本窗口与其他窗口通信消息的显示框,一个用来输入消息内容的消息编辑框,一个用来触发消息发送事件的“发送”按钮,一个输入消息发送目的窗口的编辑框。 程序开始运行后,只有一个主界面窗口,用户可以通过主界面窗口上的“新建窗口”按钮,来新建子窗口,而当前新建的子窗口总数在主界面上也有相应的区域显示。主窗口与子窗口可以自由地相互通信,而子窗口与子窗口的通信则必须通过主窗口来间接实现,即子窗口先将消息发给主窗口,然后再由主窗口转发给另一子窗口。 3. 2 设计思路 1. 主界面窗口及子窗口都选用对话框窗口,另新建一个CDialog的派生类作为子窗口类来供主窗口类调用,用以创建子窗口对象,可创建的子窗口总数限制设置为6。在主窗口类中添加一个长度为6的CDialog指针数组,用来指向由其创建的子窗口,再添加一个整型变量,用来记录当前子窗口的总数。 2. 在子窗口类中添加一个整型变量,用来记录子窗口的编号,主窗口便可以通过访问这个变量的值来区分消息是由哪个子窗口发送过来的了。当子窗口关闭时,主窗口中指向此子窗口的CDialog指针将置为NULL,同时子窗口总数减1。这时就需要重载子窗口类的WM_ON_CLOSE消息响应函数来实现了。并且 11 在此函数中还需使用一个用户自定义的子窗口关闭消息来通知主窗口进行相应的操作。 . 子窗口与主窗口的消息通信也采用自定义消息的方式实现,而消息发送3 事件则由“发送”按钮触发。编辑“发送”按钮的响应函数,在此函数中启动一个线程来向主窗口发送用户自定义的消息发送消息。由于子窗口间的通信也要通过主窗口中转,所以在此用户自定义消息的响应函数中应有一个条件判断,用以判断是否需要将刚才接收到消息转发给子窗口。 3. 3 实现步骤 3. 3. 1 创建MFC工程 ( 1 ) 打开Visual C++ 6.0,单击“文件”?“新建”命令,弹出新建对话框后,选择“工程”选项卡中的MFC AppWizard(exe)项,表示创建一个MFC应用程序工程,在“工程名称”框中输入工程名,在“位置”框中输入工程的存放路径,如图3-1所示。 图3-1 新建对话框 ( 2 ) 单击“确定”按钮,进入MFC应用程序向导 - 步骤1。选择应用程序类型为“基本对话框”,选择使用的语言为中文,如图3-2所示。 12 图3-2 MFC应用程序向导 - 步骤1 对话框 ( 3 ) 单击“下一步”按钮,进入MFC应用程序向导 - 步骤2 ,并如图3-3所示进行设置。 图3-3 MFC应用程序向导 - 步骤2 对话框 13 ( 4 ) 后面几个选项卡的内容都保持不变,可以一直单击“下一步”按钮,直到最后单击“完成”按钮;也可以直接单击“完成”按钮,弹出如图3-4所示的新建工程信息对话框。 图3-4 新建工程信息对话框 ( 5 ) 单击“确定”按钮完成工程的创建,进入VC6.0集成开发界面,如图3-5所示。 14 图3-5 VC6.0集成开发界面 3. 3. 2 编辑对话框资源 ( 1 ) 首先删除原对话框中的“确定”、“取消”按钮及“TODO:„”静态文本框。然后如图3-6所示添加控件资源,各主要控件参数如表3-1所示。 图3-6 主界面窗口对话框 15 表3-1 主界面窗口主要控件资源 ID 标题 IDC_MSG_SHOW、IDC_AIM、 \ 编辑框 IDC_MSG_EDIT IDC_CHILD_NUM 静态文本框 当前子窗口总数:0 IDC_CLEAR_RECORD 清空记录 IDC_NEW 新建窗口 按钮 IDC_CLEAR_MSG 清除 IDC_SEND_MSG 发送 ( 2 ) 插入一个新的对话框资源,其ID修改为IDD_CHILD_DIALOG,并 编辑对话框,如图3-7所示。各控件参数如表3-2所示。 图3-7 子窗口对话框 16 表3-2 子窗口主要控件资源 ID 标题 IDC_MSG_SHOW、IDC_AIM、 \ 编辑框 IDC_MSG_EDIT IDC_CLEAR_RECORD 清空记录 IDC_CLEAR_MSG 按钮 清除 IDC_SEND_MSG 发送 3. 3. 3 编写程序实现代码 ( 1 )主窗口类 1. 添加成员变量 a(通过MFC ClassWizard为编辑框IDC_MSG_SHOW、IDC_AIM、IDC_MSG_EDIT及静态文本框IDC_CHILD_NUM添加关联变量,如图3-8所示。 图3-8 17 b(然后直接在CommunicationDlg.h文件中添加其他成员变量 class CCommunicationDlg : public CDialog { // Construction public: CString m_ReceiveMsgStr; int m_MsgFrom; int m_ChildNum; „„ } 2. 重载构造函数 CCommunicationDlg::CCommunicationDlg(CWnd* pParent /*=NULL*/) : CDialog(CCommunicationDlg::IDD, pParent) { for(int i=0; iLoadIcon(IDR_MAINFRAME); } 3. 重载析构函数 18 CCommunicationDlg::~CCommunicationDlg() { for(int i=0;i < MAXNUM;i++) { if(m_pWnd!= NULL) { delete m_pWnd[i]; m_pWnd[i]= NULL; } } CDialog::~CDialog(); } 4. 给按钮添加消息响应函数 a(双击“清空记录”按钮,添加相应的消息响应函数: void CCommunicationDlg::OnClearRecord() { // TODO: Add your control notification handler code here m_MsgShowStr = ""; UpdateData(false); } b(双击“新建窗口”按钮,添加相应的消息响应函数: void CCommunicationDlg::OnNew() { // TODO: Add your control notification handler code here if(m_ChildNum < MAXNUM) { m_ChildNum++; m_ChildNumStr="当前子窗口总数: "; m_ChildNumStr += m_ChildNum + '0'; UpdateData(false); /*m_pWnd[m_ChildNum-1] = new CChildDialog(); 19 m_pWnd[m_ChildNum-1]->Create(IDD_CHILD_DIALOG,this); m_pWnd[m_ChildNum-1]->ShowWindow(SW_SHOW);*/ for(int i=0; iCreate(IDD_CHILD_DIALOG,this); m_pWnd[i]->ShowWindow(SW_SHOW); ((CChildDialog*)m_pWnd[i])->m_Num = i + 1; CString TitleStr; TitleStr = "窗口"; TitleStr += i + 1 + '0'; m_pWnd[i]->SetWindowText(TitleStr); break; } } } else { AfxMessageBox("子窗口数到达限制!"); } } c(双击“清除”按钮,添加相应的消息响应函数: void CCommunicationDlg::OnClearMsg() { // TODO: Add your control notification handler code here m_MsgTextStr = ""; UpdateData(false); } d(双击“发送”按钮,添加相应的消息响应函数: 20 void CCommunicationDlg::OnSendMsg() { // TODO: Add your control notification handler code here UpdateData(); if((m_AimNum<1)||(m_AimNum>MAXNUM)||(NULL==m_pWnd[m_AimNum -1])) { AfxMessageBox("消息发送对象错误!"); return; } else if("" == m_MsgTextStr) { AfxMessageBox("消息内容不能为空!"); return; } else { ((CChildDialog*)m_pWnd[m_AimNum-1])->m_MsgShowStr += "#主窗口发来消息:"; ((CChildDialog*)m_pWnd[m_AimNum-1])->m_MsgShowStr += m_MsgTextStr + " "; ((CChildDialog*)m_pWnd[m_AimNum-1])->UpdateData(false); } m_MsgShowStr += "#向窗口"; m_MsgShowStr += m_AimNum + '0'; m_MsgShowStr += "发送消息:"; m_MsgShowStr += m_MsgTextStr + " "; UpdateData(false); } 5. 添加用户自定义消息及其相应的消息响应函数 a(首先在CommunicationDlg.h文件中定义用户消息: #define WM_CHILDDLG_CLOSE (WM_USER+100) //子窗口关闭 21 #define WM_CHILDDLG_SENDMSG (WM_USER+110) //子窗口发送消息 b(然后在//{{AFX_MSG(CCommunicationDlg)„//}}AFX_MSG之间加上用 户自定义消息的处理函数原型: afx_msg LRESULT OnChildDlgClose(WPARAM wParam,LPARAM lParam); afx_msg LRESULT OnChildDlgSendMsg(WPARAM wParam,LPARAM lParam); c(接着在CommunicationDlg.cpp文件中加上接受消息映射的宏: //{{AFX_MSG_MAP(CCommunicationDlg) „„ ON_MESSAGE(WM_CHILDDLG_CLOSE, OnChildDlgClose) ON_MESSAGE(WM_CHILDDLG_SENDMSG, OnChildDlgSendMsg) //}}AFX_MSG_MAP d(最后在CommunicationDlg.cpp.cpp中实现用户自定义消息的消息响应函 数: LONG CCommunicationDlg::OnChildDlgClose(WPARAM wParam, LPARAM lParam) { m_ChildNum--; m_ChildNumStr = "当前子窗口总数: "; m_ChildNumStr += m_ChildNum + '0'; m_pWnd[m_MsgFrom-1] = NULL; UpdateData(false); return 0; } LONG CCommunicationDlg::OnChildDlgSendMsg(WPARAM wParam,LPARAM lParam) { if(0 == m_AimNum) { m_MsgShowStr += "#窗口"; 22 m_MsgShowStr += m_MsgFrom+'0'; m_MsgShowStr += "发来消息:" + m_ReceiveMsgStr+" "; UpdateData(false); } else { ((CChildDialog*)m_pWnd[m_AimNum-1])->m_MsgShowStr += "#窗口"; ((CChildDialog*)m_pWnd[m_AimNum-1])->m_MsgShowStr += m_MsgFrom+'0'; ((CChildDialog*)m_pWnd[m_AimNum-1])->m_MsgShowStr += "发来消息:" + m_ReceiveMsgStr + " "; ((CChildDialog*)m_pWnd[m_AimNum-1])->UpdateData(false); m_MsgShowStr += "\n"; } return 0; } ( 2 ) 子窗口类 通过MFC ClassWizard为子窗口对话框IDD_CHILD_DIALOG创建一个CDialog派生类,类名为CChildDialog。 1. 添加成员变量 a(通过MFC ClassWizard为编辑框IDC_MSG_SHOW、IDC_AIM、IDC_MSG_EDIT添加关联变量,如图3-9所示。 23 图3-9 b(然后直接在ChildDialog.h文件中添加其他成员变量 class CChildDialog : public CDialog { // Construction public: CString m_ReceiveMsgStr; int m_MsgFrom; int m_Num; „„ } 2. 重载构造函数 CChildDialog::CChildDialog(CWnd* pParent /*=NULL*/) : CDialog(CChildDialog::IDD, pParent) { //{{AFX_DATA_INIT(CChildDialog) m_MsgShowStr = _T(""); m_MsgTextStr = _T(""); 24 m_AimNum = 0; //}}AFX_DATA_INIT m_Num = 0; m_MsgFrom = 0; m_ReceiveMsgStr = ""; } 3. 给按钮添加消息响应函数 a(双击“清空记录”按钮,添加相应的消息响应函数: void CChildDialog::OnClearRecord() { // TODO: Add your control notification handler code here m_MsgShowStr = ""; UpdateData(false); } b(双击“清除”按钮,添加相应的消息响应函数: void CChildDialog::OnClearMsg() { // TODO: Add your control notification handler code here m_MsgTextStr = ""; UpdateData(false); } c(双击“发送”按钮,添加相应的消息响应函数: void CChildDialog::OnSendMsg() { // TODO: Add your control notification handler code here CCommunicationDlg *pDlg; //获得主窗口指针 pDlg = (CCommunicationDlg*)AfxGetApp()->m_pMainWnd; UpdateData(); if((m_Num==m_AimNum)||(m_AimNum<0)||(m_AimNum>MAXNUM)||(m_Ai 25 mNum!=0 && NULL==pDlg->m_pWnd[m_AimNum-1])) { AfxMessageBox("消息发送对象错误!"); return; } else if("" == m_MsgTextStr) { AfxMessageBox("消息内容不能为空!"); return; } else { if(0 == m_AimNum) { m_MsgShowStr += "#向主窗口发送消息:" + m_MsgTextStr + " "; } else { m_MsgShowStr += "#向窗口"; m_MsgShowStr += m_AimNum+'0'; m_MsgShowStr += "发送消息:" + m_MsgTextStr + " "; } // 启动线程 AfxBeginThread((AFX_THREADPROC)SendMsgThreadFunc, NULL); pDlg->m_MsgFrom = m_Num; pDlg->m_AimNum = m_AimNum; pDlg->m_ReceiveMsgStr = m_MsgTextStr; } UpdateData(false); } 4. 添加线程处理函数 a(在ChildDialog.h文件中添加线程处理函数声明: 26 DWORD SendMsgThreadFunc(LPVOID pParam); b(在ChildDialog.cpp文件中添加线程处理函数: DWORD SendMsgThreadFunc(LPVOID pParam) { CCommunicationDlg *pDlg; //获得主窗口指针 pDlg = (CCommunicationDlg*)AfxGetApp()->m_pMainWnd; //向主窗口发送WM_CHILDDLG_SENDMSG自定义消息 pDlg->PostMessage(WM_CHILDDLG_SENDMSG,0,0); return 0; } 3. 4 程序运行结果 程序编译运行后,弹出如图3-10所示的运行主界面。 图3-10 程序主界面 27 3. 4. 1 窗口的创建 a(点击“新建窗口”按钮,弹出如图3-11所示的子对话框窗口。 图3-11 子对话框窗口 b(继续点击“新建窗口”按钮,可继续创建子窗口,最多可创建6个子对话框窗口,如图3-12所示。 28 图3-12 c(此时若继续点击“新建窗口”按钮,则会弹出如图3-13所示的警告对话框。 图3-13 d(关闭子窗口是,在主窗口上显示的当前子窗口数也会随之变化。如图3-14所示。 图3-14 3. 4. 2 窗口的通信 ( 1 ) 子窗口与主窗口间的通信 29 图3-15 子窗口与主窗口间的通信演示 30 ( 2 ) 子窗口与子窗口间的通信 图3-16 子窗口与子窗口间的通信示例1 31 图3-17 子窗口与子窗口间的通信示例2 32 结论 由上文的多线程通信程序开发实例可以看出,在用Visual C++6.0 MFC 来进行多线程通信程序开发时, 工程成功创建后所产生的代码都是标准化的模块,而需要我们自己做的具体编码式工作很少。我们只需要根据应用程序的需要,完成相应变量的定义,线程处理函数及全局函数的声明与具体实现,并借助资源编辑器来设计好用户界面,并为各控件添加相应的消息响应处理函数。所以,Visual C++6.0 MFC是进行多线程通信程序开发的有力工具,我们在开发多线程通信程序时,应尽量选择MFC来进行程序开发,以简化程序开发的过程,并减少开发的工作量和难度。 此外,在进行多线程程序开发时,还要注意合理地使用多线程,切不可盲目地滥用多线程。因为,更多的线程需要更多的内存空间,并且管理这些线程还要求额外的CPU开销。如果两个非常活跃的线程为了抢夺对CPU的控制权,在线程切换时会消耗很多的CPU资源,反而会降低系统的性能。但对于耗时或大量占用处理器并阻塞了用户界面操作的线程( 例如等待文件I/ O 操作),则可用一个单独的线程来完成。这样,就可将CPU 时间让出来,使程序获得更好的性能。 33 参考文献(References) [1] 骆斌,费翔林(多线程技术的研究与应用[J].计算机研究与发展,2000, (04)( [2] 杨立峰(基于MFC的多线程编程技术[J] . 扬州职业大学学报,2008, (02)( [3] 郝文化,王浩强(Windows 多线程编程技术与实例[M].北京:中国水利水电出版社, 2005( [4] 王明福,余苏宁(Visual C++程序设计[M](北京:高等教育出版,2004( 34 致谢 大学四年即将宣布结束,这份毕业论文也将是我在校的最后一份作品,在这期间,我不断地成长,成熟和成功,当然也就少不了家里的亲朋好友,学校的领导、老师和同学的关照及督促。 首先,感谢我的父母,因为他们多年的无私奉献,才有我今年的出人头地,才荣幸地从伟大的母校——南昌大学毕业,这些年,他们苍老了,但他们把青春留给我延续,我很感动很幸福。 其次,我要感谢南昌大学的每一位领导、老师、同学及每一位为学校贡献的可爱人,因为他们的支持、给予和督促,让我独立自主,顽强奋斗,才有今天的完美结束符。这里特别要感谢我的班主任**老师,我的导师**教授和我那些最亲近的同学朋友,四年的风风雨雨里,是他们时刻的帮助和鼓励才让我坚强起来。 最后,感谢我的祖国,感谢生我养我的热土,感谢我的亲朋好友,感谢…… 35
本文档为【[计算机软件及应用]2012毕设论文——基于VC的多线程通信程序设计】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_686908
暂无简介~
格式:doc
大小:439KB
软件:Word
页数:0
分类:生活休闲
上传时间:2017-09-26
浏览量:25