域或共享资源时,也会存在同样的问题。
因此,在多线程应用程序中,常常需要采取一些措施来同步线程的执行。
等待函数 Win32 API提供了一组能使线程阻塞其自身执行的等待函数。
这些函数只有在作为其参数的一个或多个同步对象见下小节产生信号时才会返回。
在超过规定的等待时间后,不管有无信号,函数也都会返回。
在等待函数未返回时,线程处于等待状态,此时线程只消耗很少的CPU时间。
使用等待函数即可以保证线程的同步,又可以提高程序的运行效率。
最常用的等待函数是WaitForSingleObject,该函数的声明为: DWORD WaitForSingleObjectHANDLE hHandle DWORD dwMilliseconds 参数hHandle是同步对象的句柄。
参数dwMilliseconds是以毫秒为单位的超时间隔,如果该参数为0,那么函数就测试同步对象的状态并立即返回,如果该参数为INFINITE,则超时间隔是无限的。
同步对象 同步对象用来协调多线程的执行,它可以被多个线程共享。
线程的等待函数用同步对象的句柄作为参数,同步对象应该是所有要使用的线程都能访问到的。
同步对象的状态要么是有信号的,要么是无信号的。
同步对象主要有三种:事件、mutex和信号灯。
事件对象Event是最简单的同步对象,它包括有信号和无信号两种状态。
在线程访问某一资源之前,也许需要等待某一事件的发生,这时用事件对象最合适。
例如,只有在通信端口缓冲区收到数据后,监视线程才被激活。
mutex对象的状态在它不被任何线程拥有时是有信号的,而当它被拥有时则是无信号的。
mutex对象很适合用来协调多个线程对共享资源的互斥访问mutuallyexclusive。
信号灯对象维护一个从0开始的计数,在计数值大于0时对象是有信号的,而在计数值为0时则是无信号的。
信号灯对象可用来限制对共享资源进行访问的线程数量。
线程用CreateSemaphore函数来建立信号灯对象,在调用该函数时,可以指定对象的初始计数和最大计数。
在建立信号灯时也可以为对象起个名字,别的进程中的线程可以用OpenSemaphore函数打开指定名字的信号灯句柄。
关键节和互锁变量访问 关键节Critical Seciton与mutex的功能类似,但它只能由同一进程中的线程使用。
关键节可以防止共享资源被同时访问。
进程负责为关键节分配内存空间,关键节实际上是一个CRITICAL_SECTION型的变量,它一次只能被一个线程拥有。
在线程使用关键节之前,必须调用InitializeCriticalSection函数将其初始化。
如果线程中有一段关键的代码不希望被别的线程中断,那么可以调用EnterCriticalSection函数来申请关键节的所有权,在运行完关键代码后再用LeaveCriticalSection函数来释放所有权。
如果在调用EnterCriticalSection时关键节对象已被另一个线程拥有,那么该函数将无限期等待所有权。
利用互锁变量可以建立简单有效的同步机制。
使用函数InterlockedIncrement和InterlockedDecrement可以增加或减少多个线程共享的一个32位变量的值,并且可以检查结果是否为0。
线程不必担心会被其它线程中断而导致错误。
如果变量位于共享内存中,那么不同进程中的线程也可以使用这种机制。
串行通信与重叠I/OWin 32 系 统 为 串 行 通 信 提 供 了 全 新 的 服 务 。
传 统 的 OpenComm 、ReadComm 、 WriteComm 、 CloseComm 等 函 数 已 经 过 时 ,WM_COMMNOTIFY消息也消失了。
取而代之的是文件I/O函数提供的打开和关闭通信资源句柄及读写操作的基本接口。
新的文件I/O函数CreateFile、ReadFile、WriteFile等支持重叠式输入输出,这使得线程可以从费时的I/O操作中解放出来,从而极大地提高了程序的运行效率。
串行口的打开和关闭 Win 32系统把文件的概念进行了扩展。
无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。
该函数的声明为:HANDLE CreateFileLPCTSTR lpFileName // 文件名DWORD dwDesiredAccess // 访问模式DWORD dwShareMode // 共享模式PSECURITY_ATTRIBUTES lpSecurityAttributes // 通常为NULLDWORD dwCreationDistribution // 创建方式DWORD dwFlagsAndAttributes // 文件属性和标志HANDLE hTemplateFile // 临时文件的句柄,通常为NULL 如果调用成功,那么该函数返回文件的句柄,如果调用失败,则函数返回INVALID_HANDLE_VALUE。
串行口的初始化 在打开通信设备句柄后,常常需要对串行口进行一些初始化工作。
这需要通过一个DCB结构来进行。
DCB结构包含了诸如波特率、每个字符的数据位数、奇偶校验和停止位数等信息。
在查询或配置置串行口的属性时,都要用DCB结构来作为缓冲区。
调用GetCommState函数可以获得串口的配置,该函数把当前配置填充到一 个 DCB 结 构 中 。
一 般 在 用 CreateFile 打 开 串 行 口 后 , 可 以 调 用GetCommState函数来获取串行口的初始配置。
要修改串行口的配置,应该先修改DCB结构,然后再调用SetCommState函数用指定的DCB结构来设置串行口。
在用ReadFile和WriteFile读写串行口时,需要考虑超时问题。
如果在指定的时间内没有读出或写入指定数量的字符,那么ReadFile或WriteFile的操作就会结束。
要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构。
调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。
重叠I/O在用ReadFile和WriteFile读写串行口时,既可以同步执行,也可以重叠(异步)执行。
在同步执行时,函数直到操作完成后才返回。
这意味着在同步执行时线程会被阻塞,从而导致效率下降。
在重叠执行时,即使操作还未完成,调用的函数也会立即返回。
费时的I/O操作在后台进行,这样线程就可以干别的事情。
例如,线程可以在不同的句柄上同时执行I/O操作,甚至可以在同一句柄上同时进行读写操作。
“重叠”一词的含义就在于此。
ReadFile函数只要在串行口输入缓冲区中读入指定数量的字符,就算完成操作。
而WriteFile函数不但要把指定数量的字符拷入到输出缓冲中,而且要等这些字符从串行口送出去后才算完成操作。
ReadFile和WriteFile函数是否为执行重叠操作是由CreateFile函数决定的。
如果在调用CreateFile创建句柄时指定了FILE_FLAG_OVERLAPPED标志,那么调用ReadFile和WriteFile对该句柄进行的读写操作就是重叠的,如果未指定重叠标志,则读写操作是同步的。
上一篇:
[精品]使用HTML5创建Flex的离线应用程序
下一篇:
4种小菜缓解孕妈咪不适