Win32 中最具弹性的同步机制就属 events 对象了。Event 对象是一种核心对象,它的唯一目的就是成为激发状态或未激发状态。这两种状态全由程序来控制,不会成为 Wait...() 函数的副作用。
    Event 对象之所以有大用途,正是因为它们的状态完全在你掌控之下。Mutexes 和 semaphores 就不一样了, 它们的状态会因为诸如WaitForSingleObject() 之类的函数调用而变化。所以,你可以精确告诉一个event 对象做什么事,以及什么时候去做。
    Event 对象被运用在多种类型的高级 I/O 操作中。你可以在第6章看到一些例子。Event 对象也可以用来设计你自己的同步对象。
    为了产生一个 event 对象,你必须调用 CreateEvent():
HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTES lpEventAttributes,
    BOOL bManualReset,
    BOOL bInitialState,
    LPCTSTR lpName
);
参数
    lpEventAttributes     安全属性。NULL 表示使用默认属性。该属性在Windows 95 中会被忽略。
    bManualReset         如为 FALSE,表示这个 event 将在变成激发状态(因而唤醒一个线程)之后,自动重置(reset)为非激发状态。如果是 TRUE,表示不会自动重置,必须靠程序操作(调用 ResetEvent())才能将激发状态的 event 重置为非激发状态。
    bInitialState         如为 TRUE,表示这个 event 一开始处于激发状态。如为 FALSE,则表示这个 event 一开始处于非激发状态。
    lpName Event         对象的名称。任何线程或进程都可以根据这个文字名称,使用这一 event 对象。
返回值
    如果调用成功,会传回个 event handle,GetLastError() 会传回 0。如果lpName 所指定的 event 对象已经存在,CreateEvent() 传回的是该 event handle , 而不会产生一个新的。这时候 GetLastError() 会传回ERROR_ALREADY_EXISTS。如果 CreateEvent() 失败,传回的是 NULL,GetLastError() 可以获得更进一步的失败信息。
    SetEvent()         把 event 对象设为激发状态
    ResetEvent()         把 event 对象设为非激发状态(译注:在此我要提醒读者,"Reset" 的意思是“设定为非激发状态”,而非“重新设定为激发状态”。)
    PulseEvent()         如果是一个 Manual Reset Event:把 event 对象设为激发状态,唤醒“所有”等待中的线程,然后 event 恢复为非激发状态。如果是一个 Auto Reset Event:把 event 对象设为激发状态,唤醒“一个”等待中的线程,然后 event 恢复为非激发状态
    如果你选择一个新的 event 类型(若不是“Automatic”就是“Manual”),原有的 event 对象和线程统统会被摧毁,程序重新产生出新的 event 和新的线程。
    重点:操作系统会强迫让等待中的线程有轮番更替的机会。对于本章介绍的所有同步机制,这都是一个重要的行为。如果操作系统没有强迫实现某种层次的公平性,可能会有某个线程不断获得执行机会,而某个线程一直未能获得 CPU 的青睐。这种情况被称为starvation(饥饿)。
    如果你面对一个 AutoReset event 对象调用 SetEvent() 或 PulseEvent(),而彼时并没有任何线程正在等待,会怎样?EVENTTST 程序并没有实地论证这一点。这种情况下这个 event 会被遗失。换句话说,除非有线程正在等待,否则 event 不会被保存下来。这样的行为使得“要求苏醒”的请求颇容易被遗失掉。例如,线程A累加一个计数器,之后调用 WaitForSingleObject() 等待一个 event 对象。如果在这些动作之间发生 context switch,线程B起而执行,它检查计数器内容然后对着同一个 event 对象调用 PulseEvent()。这时候这个“要求苏醒”的请求会遗失掉,因为这个 pulse 不会被储存起来(译注:因为还没有线程处于等待状态嘛)。
    另一种情况可能会引起死锁。假设“receiver”线程检查队列中是否有字符,这时候发生 context switch,切换到“sender”线程,它对一个 event 对象进行 pulse 操作,这时候又发生 context switch,回到 receiver 线程,调用WaitForSingleObject(),等待 event 对象。由于这个动作发生在 sender 线程激发 event 之后,所以 event 会遗失,于是 receiver 永远不会醒来,程序进入死锁状态。这正是 semaphore 之所以被创造用以解决问题的地方。
从 Worker 线程中显示输出
    此刻我想先打个岔,请各位看看 EVENTTST 如何让 worker 线程(那三个等待中的线程)把字符串放到列表框(listbox)中。列表框的消息循环总是被单一线程(也就是程序的主线程)掌管,虽然这并非绝对必要,但是让主线程负责所有的屏幕更新工作,是相当理想的。
    我在程序中定义了一个消息,名为 WM_PLEASE_UPDATE。当 worker线程认为需要把一笔新的项目放到列表框中时,就送这个消息给主线程。Worker 线程使用 SendMessage() 完成这件事情,以便制造出一种“函数调用”的效果。在主线程处理完毕该消息之前,SendMessage() 不会返回,所以我们可以保证所有的输出有条不紊,不至于乱了次序。
    请注意,我一直仰赖一件事实:所有的数据可以被所有的线程取用。我使用sprintf() 在线程的堆栈中产生一个字符串,然后将此字符串地址以 SendMessage()送出。主线程在更新列表框的画面时,即使用到这个地址,一旦主线程完成这个消息的处理,SendMessage() 便返回,worker 线程于是继续进行下去。
    想象一下,如果我以 PostMessage() 代替 SendMessage(),会发生什么情况?由于 PostMessage() 会立刻返回,所以当主线程抓取字符串内容要显示时,或许该字符串内容早已又被 worker 线程改写了。这就是多线程序设计中最常见的一种两难取舍:在最佳速度和最佳安全性之间取舍。在这里我宁愿选择比较慢但是比较安全的做法。

第4章 同步控制 Synchronization ----事件(Event Objects)的更多相关文章

  1. 第4章 同步控制 Synchronization ----同步机制的摘要

    同步机制摘要Critical Section Critical section(临界区)用来实现"排他性占有".适用范围是单一进程的各线程之间.它是:  一个局部性对象,不是一个核 ...

  2. 第4章 同步控制 Synchronization ---哲学家进餐问题(The Dining Philosophers)

    哲学家进餐问题是这样子的:好几位哲学家围绕着餐桌坐,每一位哲学家要么思考,要么等待,要么就吃饭.为了吃饭,哲学家必须拿起两支筷子(分放于左右两端).不幸的是,筷子的数量和哲学家相等,所以每支筷子必须由 ...

  3. 第4章 同步控制 Synchronization ----critical section 互斥区 ,临界区

    本章讨论 Win32 同步机制,并特别把重点放在多任务环境的效率上.撰写多线程程序的一个最具挑战性的问题就是:如何让一个线程和另一个线程合作.除非你让它们同心协力,否则必然会出现如第2章所说的&quo ...

  4. 第4章 同步控制 Synchronization ----Interlocked Variables

    同步机制的最简单类型是使用 interlocked 函数,对着标准的 32 位变量进行操作.这些函数并没有提供"等待"机能,它们只是保证对某个特定变量的存取操作是"一个一 ...

  5. 第4章 同步控制 Synchronization ----信号量(Semaphore)

    许多文件中都会提到 semaphores(信号量),因为在电脑科学中它是最具历史的同步机制.它可以让你陷入理论的泥淖之中,教授们则喜欢问你一些有关于信号量的疑难杂 症.你可能不容易找到一些关于 sem ...

  6. 第4章 同步控制 Synchronization ----互斥器(Mutexes)

    Win32 的 Mutex 用途和 critical section 非常类似,但是它牺牲速度以增加弹性.或许你已经猜到了,mutex 是 MUTual EXclusion 的缩写.一个时间内只能够有 ...

  7. 第4章 同步控制 Synchronization ----死锁(DeadLock)

    Jeffrey Richter 在他所主持的 Win32 Q&A 专栏(Microsoft Systems Journal,1996/07)中曾经提到过,Windows NT 和 Window ...

  8. nginx&http 第二章 ngx 事件event处理 数据结构

    ngx_event.c :这个文件主要放置Nginx事件event模块的核心代码. 包含:进程事件分发器(ngx_process_events_and_timers).事件模块的模块和配置.模块初始化 ...

  9. 【温故知新】c#事件event

    从上一篇文章[温故知新]C#委托delegate可知,委托delegate和事件Event非常的相似,区别就是event关键字,给delegate穿上了个“马甲”. 让我们来看官方定义: 类或对象可以 ...

随机推荐

  1. 手把手封装数据层之DButil数据库连接的封装

    最近这段时间一直在用SSM框架做增删改查,突然想把以前还不会用框架的时候,综合百度和各种资料结合API文档抄袭而来的数据层的封装分享给大家.这边先封装一个DButil. 我这个封装就是烂大街的那种,没 ...

  2. 关于DreamWeaver CS6.0 + PhoneGap 之移动开发环境搭建

    原博客地址为:http://blog.csdn.net/alovebtoc/article/details/9315437  HTML5已经逆袭了移动开发,近期有幸布置PhoneGap的环境搭载,其实 ...

  3. Java常见算法整理

    兔子问题(斐波那契数列规律) 台阶问题 (兔子问题变种,递归规律) 素数问题(判断素数.质数方式) 水仙花数问题(数字分解) 查找算法(二分查找) 排序算法(选择排序,冒泡排序,快速排序) 兔子问题, ...

  4. Java基础语法(一)---关键字、常量、变量、运算符

    一.关键字 定义:被Java赋予了特殊含义的单词. 特点:体现上都是英文小写. 1. 用于定义数据类型的关键字 基本数据类型: 整数类型:byte  short  int  long   浮点类型:f ...

  5. python requests 官方文档

    链接:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html

  6. 获取ip地址及城市信息

    大家好,今天给大家分享的是一个简单的知识获取登录用户的ip地址及城市信息,lz是一个小白,如果有哪些错误的地方  欢迎大家指出 东西很简单,直接上代码 [HttpPost] public string ...

  7. 创建DNS子域及view

    author:JevonWei 版权声明:原创作品 子域 子域同父域在同一个服务器上 新建子域jevon.danran.com vim /etc/named.rfc1912.zones zone &q ...

  8. display:inline-block间隙问题

    display:inline-block 是让块级元素变成行内元素 在一行显示, 然而不幸的是,它并没有得到所有浏览器的支持,比如ie6.7和古老一点的firefox完全无视它,由于firefox的老 ...

  9. 【.net 深呼吸】WPF 中的父子窗口

    与 WinForm 不同,WPF 并没有 MDI 窗口,但 WPF 的窗口之间是可以存在“父子”关系的. 我们会发现,Window 类公开了一个属性叫 Owner,这个属性是可读可写的,从名字上我们也 ...

  10. 九度OJ 1006 ZOJ

    #include <iostream> #include <string> using namespace std; int getO(string str,int & ...