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. BestCoder Round #32_1001 以及 hdu 5182

    http://bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=570&pid=1001 http://acm.hdu. ...

  2. 流畅python学习笔记:第十九章:动态属性和特性

    首先来看一个json文件的读取.书中给出了一个json样例.该json文件有700多K,数据量充足,适合本章的例子.文件的具体内容可以在http://www.oreilly.com/pub/sc/os ...

  3. SpringMVC简单入门

    SpringMVC简单入门 与大家分享一下最近对SpringMVC的学习,希望本文章能对大家有所帮助. 首先什么是SpringMVC? Spring 为展现层提供的基于MVC设计理念的优秀的Web框架 ...

  4. python3 requests 获取 拉勾工作数据

    #-*- coding:utf-8 -*- __author__ = "carry" import requests,json for x in range(1, 15): url ...

  5. 插入排序与希尔排序Java实现

    public class TestMain { public static void main(String[] args) { Integer[] a = new Integer[5000]; fo ...

  6. h5新增html标签语义

    H5新增常用标签<body> <header>...</header> <nav>...</nav> <article> < ...

  7. Redis集群的相关概念

    1.1 redis-cluster架构图 架构细节: (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. (2)节点的fail是通过集群中超过半数的节 ...

  8. makefile中":=","=","?=","+=" 之间的区别

    区别:  := 有关位置的等于,值取决于当时位置的值 = 无关位置的等于,值永远等于最后的值 ?= 是如果没有被赋值过就赋予等号后面的值+= 是添加等号后面的值 '=':无关位置的等于 比如: x = ...

  9. 客户端与服务端,java与Android跨平台服务

  10. 201521123082 《Java程序设计》第11周学习总结

    201521123082 <Java程序设计>第11周学习总结 标签(空格分隔):java 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. Answe ...