2023年6月22日发(作者:)
多线程在VC++串⼝通信程序中的应⽤作者:赵向峰 边信黔 施⼩成
摘要:本⽂通过⼀机房监控系统程序中串⼝通信对多线程的应⽤来介绍 Windows 9X/NT 操作系统中多线程的应⽤和VC++对多线程的⽀持。关健词: 多线程,串⼝通信1 概述在现代的各种实时监控系统或通信系统中,实时性是其最主要的要求之⼀,所以在程 序设计中通过各种⽅式以满⾜这种要求成为出发点之⼀。Windows 9X/NT 是抢先式的多任 务操作系统,程序对 CPU 的占⽤时间由系统决定。多任务指的是系统可以同时运⾏多个进 程,每个进程⼜可以同时执⾏多个线程。进程是应⽤程序的运⾏实例,拥有⾃⼰的地址空间。 每个进程拥有⼀个主线程, 同时还可以建⽴其他的线程。线程是操作系统分配 CPU时间的 基本实体,每个线程占⽤的 CPU 时间由系统分配,系统不停的在线程之间切换。进程中的 线程共享进程的虚拟地址空间,可以访问进程的资源,处于并⾏执⾏状态,它的应⽤可以简 化应⽤程序的结构把某些复杂的、运算放到后台去做,从⽽⼤⼤提⾼应⽤程序的响应能⼒。所以在window9X/NT 下利⽤ VC++对 RS -322 串⼝编程成为⼀常⽤的⽅法。2 VC++对多线程的⽀持在 VC++编程中通常使⽤ MFC 进⾏开发以减少代码的书写。在 VC++6.0下,MFC 应⽤ 程序的线程由 CWinThread 对象表⽰。VC++把线程分为两种:⽤户界⾯线程(GUI)和⼯作 者(worker)线程。⽤户界⾯线程能够提供界⾯和⽤户交互,通常⽤于处理⽤户输⼊并相应各 种事件和消息;⽽⼯作者线程主要⽤来处理程序的后台任务。程序⼀般不需要直接创建CWinThread对象,通过调⽤ AfxBeginThread()函数就会⾃动创建⼀个 CWinThread 对象,从 ⽽开始⼀个进程。创建上述的两种线程都利⽤这个函数。线程的终⽌取决于下列事件之⼀: 线程函数返回;线程调⽤ ExitThread()退出;异常情况下⽤线程的句柄调⽤ TerminateThread() 退出;线程所属的进程被终⽌。3 多线程在串⼝通信中的应⽤3.1 串⼝通信对线程同步的要求因为同⼀进程的所有线程共享进程的虚拟地址空间,⽽在 Windows 9X/NT 系统下线程 是汇编级中断,所以有可能多个线程同时访问同⼀个对象。这些对象可能是全局变量,MFC 的对象,MFC 的 API 等。串⼝通信的⼏个特点决定了必须采⽤措施来同步线程的执⾏。 串⼝通信中,对于每个串⼝对象,只有⼀个缓冲区,发送和接收都要⽤到,必须建⽴起同步 机制,使得在⼀个时候只能进⾏⼀种操作,否则通信就会出错。进⾏串⼝通信处理的不同线 程之间需要协调运⾏。如果⼀个线程必须等待另⼀个线程结束才能运⾏,则应该挂起该线程以减少对 CPU资源的占⽤,通过另⼀进程完成后发出的信号(线程间通信)来激活。VC++提供了同步对象来协调多线程的并⾏,常⽤的有以下⼏种:CSemaphore:信号灯对象,允许⼀定数量的线程访问某个共享资源,常⽤来控制访问共享资源的线程数量。CMutex:互斥量对象,⼀个时刻⾄多只允许⼀个线程访问某资源,未被占⽤时处于有信号状态,可以实现对共享资源的互斥访问。CEvent:事件对象,⽤于使⼀个线程通知其他线程某⼀事件的发⽣,所以也可以⽤来封 锁对某⼀资源的访问,直到线程释放资源使其成为有信号状态。适⽤于某⼀线程等待某事件发⽣才能执⾏的场合。CCriticalSection:临界区对象,将⼀段代码置⼊临界区,只允许最多⼀个线程进⼊执⾏这段代码。⼀个临界区仅在创建它的进程中有效。3.2 等待函数Win32 API 提供了能使线程阻塞其⾃⾝执⾏的等待函数,等待其监视的对象产⽣⼀定的 信号才停⽌阻塞,继续线程的执⾏。其意义是通过暂时挂起线程减少对 CPU 资源的占⽤。 在某些⼤型监控系统中,串⼝通信只是其中事务处理的⼀部分,所以必须考虑程序执⾏效率 问题,当串⼝初始化完毕后,就使其处于等待通信事件的状态,减少消耗的 CPU 时间,提 ⾼程序运⾏效率。常⽤的等待函数是 WaitForSingleObject()和 WaitForMultipleObjects(),前者可监测单个同步对象,后者可同时监测多个同步对象。3.3 串⼝通信的重叠 I/O⽅式MFC 对于串⼝作为⽂件设备处理,⽤ CreateFile()打开串⼝,获得⼀个串⼝句柄。打开 后 SetCommState()进⾏端⼝配置,包括缓冲区设置,超时设置和数据格式等。成功后就可以 调⽤函数 ReadFile()和 WriteFile()进⾏数据的读写,⽤WaitCommEvent()监视通信事件。 CloseHandle()⽤于关闭串⼝。在 ReadFile()和 WriteFile()读写串⼝时,可以采取同步执⾏⽅ 式,也可以采取重叠 I/O⽅式。同步执⾏时,函数直到执⾏完毕才返回,因⽽同步执⾏的其 他线程会被阻塞,效率下降;⽽在重叠⽅式下,调⽤的读写函数会⽴即返回,I/O操作在后 台进⾏,这样线程就可以处理其他事务。这样,线程可以在同⼀串⼝句柄上实现读写操作,实现"重叠"。使⽤重叠 I/O⽅式时,线程要创建 OVERLAPPED 结构供读写函数使⽤,该结构最重要 的成员是 hEvent 事件句柄。它将作为线程的同步对象使⽤,读写函数完成时 hEvent 处于有信号状态,表⽰可进⾏读写操作;读写函数未完成时,hEvent 被置为⽆信号。4 程序关键代码的实现程序专门建⽴了⼀个串⼝通信类,下⾯给出关键成员函数的核⼼代码。BOOL InitComm() //串⼝初始化,这⾥只给出关键步骤的代码,下同{ HANDLE m_hComm; COMMTIMEOUTS m_CommTimeouts; m_hComm = CreateFile("COM1", //在这⾥只使⽤串⼝ 1 GENERIC_READ | GENERIC_WRITE, //打开类型为可读写 0, //以独占模式打开串⼝ NULL, //不设置安全属性 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, //重叠 I/O⽅式 0); if (m_hComm == INVALID_HANDLE_VALUE) //打开不成功 { return FALSE; } m_tervalTimeout = 1000; //进⾏超时设置,读者应根据⾃⼰的实际需要设置 m_talTimeoutMultiplier = 500; m_talTimeoutConstant = 5000; m_otalTimeoutMultiplier = 500; m_otalTimeoutConstant = 5000; if (!SetCommTimeouts(m_hComm, &m_CommTimeouts)) {CloseHandle(m_hComm); return FALSE;} PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT |
PURGE_TXABORT); //清缓冲 return TRUE;
}以上是专门针对 COM1 的初始化,如果要利⽤同⼀函数对不同串⼝初始化,则要在初始化前先进⼊代码临界区,以保证在某⼀时刻只进⾏⼀个串⼝的初始化。在串⼝初始化成功后,就可以建⽴监控线程处理串⼝通信事件。下⾯是该线程的关键代码UINT CommThread(LPVOID pParam) //⽤于监控串⼝的⼯作者线程{ BOOL bResult = FALSE; if (m_hComm) //查看端⼝是否打开,这⾥ m_hComm同上,作者在这⾥做了简化 PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT |PURGE_TXABORT); for (;;) //只要线程运⾏,就处于监视端⼝⾏为的⽆限循环 { bResult = WaitCommEvent(m_hComm, &Event, &m_ov); //m_ov 是 OVERLAPPED 类型的成员变量 if (!bResult) {//进⾏出错处理 ... } else { Event = WaitForMultipleObjects(4, m_hEvent, FALSE, INFINITE); //⽆限等待设定的事件发⽣,数组 m_hEvent 根据需要定义了须响应的接收,发送, //关闭端⼝事件和 OVERLAPPED 类型的 hEvent 事件 switch (Event) { //读写事件的响应处理过程,在此略 } } return 0; }}这样监控主程序就可以使⽤ AfxBeginThread()函数来产⽣ CommThread 串⼝监控线程。如果 要实现对所有端⼝的同时监控,可以分别对端⼝建⽴监控线程、5 ⼩结作为⼀个机房监控系统的组成部分,通过多线程的应⽤本串⼝通信程序在 VC++6.0 下编译通过,在使⽤ windows 98/NT 的局域⽹⾥运⾏良好。
发布者:admin,转转请注明出处:http://www.yc00.com/news/1687427097a9238.html
评论列表(0条)