211. Orchard学习 二 2、ManualResetEvent 与 AutoResetEvent

时间:2023-02-24 23:20:50

一、Orchard里异步请求处理线程队列的控制

Orchard的Orchard.WarmupStarter模块,为HttpApplication.BeginRequest时间附加了一个异步处理事件:BeginBeginRequest。

   1: /// <summary>

   2: /// 启动 System.Web.HttpApplication.BeginRequest 的异步处理的 System.Web.BeginEventHandler

   3: /// System.Web.HttpApplication.BeginRequest 在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生。 

   4: /// </summary>

   5: private IAsyncResult BeginBeginRequest(object sender, EventArgs e, AsyncCallback cb, object extradata)

   6: {

   7:     // host is available, process every requests, or file is processed

   8:     if (!InWarmup() || WarmupUtility.DoBeginRequest(_context))

   9:     {

  10:         /***

  11:          *  !InWarmup()  不在预热中

  12:          *  WarmupUtility.DoBeginRequest(_context) 找到了与请求URL匹配的静态文件资源

  13:          */

  14:         var asyncResult = new DoneAsyncResult(extradata);

  15:         cb(asyncResult);

  16:         return asyncResult;

  17:     }

  18:     else

  19:     {

  20:         // this is the "on hold" execution path

  21:         var asyncResult = new WarmupAsyncResult(cb, extradata);

  22:         Await(asyncResult.Completed);

  23:         return asyncResult;

  24:     }

  25: }

在请求开始时,检查系统状态,“不在预热中”或者找到了与请求的URL匹配的静态文件资源,则构造一个 DoneAsncResult类型实例,执行并返回结果状态。如果正在预热,或没有匹配的静态文件资源,将请求加入一个待执行队列,直到预热完成发出信号后再执行(这个见Orchard.WarmupStarter.Starter.LaunchStartupThread())。

我们再来看 DoneAsyncResult和WarmupAsyncResult

   1: /// <summary>

   2: /// AsyncResult for "on hold" request (resumes when "Completed()" is called)

   3: /// </summary>

   4: private class WarmupAsyncResult : IAsyncResult

   5: {

   6:     /****************

   7:     // 通知一个或多个正在等待的线程已发生事件,处理器类型。      “等待线程 事件通知 处理器”.

   8:     // AutoResetEvent 继承自 ,EventWaitHandle:WaitHandle , 表示一个线程同步事件。 

   9:     // EventWaitHandle 主要操作方法: Set() , Reset()。

  10:     // WaitHandle 封装等待对共享资源的独占访问的操作系统特定的对象

  11: 

  12:     // 初始化一个  “等待线程 事件通知 处理器” 。

  13:     // 参数 initialState ,初始状态是否为 终止:

  14:     //     true   终止  ,即目前已无事件发生,无需等待,线程继续运行

  15:     //     false  非终止,即目前有事件发生,需等待,线程暂停运行

  16:     ****************/

  17:     private readonly EventWaitHandle _eventWaitHandle = new AutoResetEvent(false/*initialState*/);

  18:  

  19:     //...省略若干代码

  20: }

  21:  

  22: /// <summary>

  23: /// 已 “执行完成 or 正在处理” 的异步操作状态

  24: /// Async result for "ok to process now" requests

  25: /// </summary>

  26: private class DoneAsyncResult : IAsyncResult

  27: {

  28:     /****************

  29:     // 通知一个或多个正在等待的线程已发生事件,处理器类型。      “等待线程 事件通知 处理器”.

  30:     // ManualResetEvent 继承自 ,EventWaitHandle:WaitHandle , 表示一个线程同步事件。 

  31:     // EventWaitHandle 主要操作方法: Set() , Reset()。

  32:     // WaitHandle 封装等待对共享资源的独占访问的操作系统特定的对象

  33: 

  34:     // 初始化一个  “等待线程 事件通知 处理器” 。

  35:     // 参数 initialState ,初始状态是否为 终止:

  36:     //     true   终止  ,即目前已无事件发生,无需等待,线程继续运行

  37:     //     false  非终止,即目前有事件发生,需等待,线程暂停运行

  38:     ****************/

  39:     private static readonly WaitHandle _waitHandle = new ManualResetEvent(true/*initialState*/);

  40:  

  41:     //...省略若干代码

  42: }

他们分别实例化了AutoResetEvent 和 ManualResetEvent事件通知处理器。AutoResetEvent 和 ManualResetEvent 都继承自 EventWaitHandle,WaitHandle ,表示一个线程同步事件通知器。 通俗的讲,就是程序中需要跨多个线程处理协调事件时,一个用来通知协调事件处理状态的处理器。

EventWaitHandle 主要操作方法:

  • Set() :将事件状态设置为终止状态,即目前已无事件发生,无需等待,其他等待中的线程继续运行;
  • Reset() :将事件状态设置为非终止状态,即目前有事件发生,需等待,其他线程暂停运行;
  • WaitOne():阻塞当前线程 ,直到 _resetEventHandler 收到新的事件信号(即 set or reset);

AutoResetEvent 和 ManualResetEvent初始化时,参数 initialState ,初始状态是否为 终止。其表意如下:

  • true 终止 ,即目前已无事件发生,无需等待,线程继续运行,
  • false 非终止,即目前有事件发生,需等待,线程暂停运行

二、AutoResetEvent 与 ManualResetEvent

看起来 AutoResetEvent 和 ManualResetEvent 很像,我们通过一个demo来看看他们的区别

Class Program 与 CLaunchStartupThread

   1: class Program

   2: {

   3:     static void Main(string[] args)

   4:     {

   5:         Common.ConsoleWriteLine("----------------------", false);

   6:         Common.ConsoleWriteLine(" S 暂停      R 继续   ", false);

   7:  

   8:         var resetEventHandler = new ResetEventHandle<ManualResetEvent>(new ManualResetEvent(true));

   9:         //var resetEventHandler = new ResetEventHandle<AutoResetEvent>(new AutoResetEvent(true));

  10:         Common.ConsoleWriteLine(" Type: " + resetEventHandler.CurTypeName, false);

  11:         Common.ConsoleWriteLine(" MainThread: ");

  12:         Common.ConsoleWriteLine("\r\n", false);

  13:         //创建线程

  14:         Common.CreateThreads(resetEventHandler.Run, 3);

  15:  

  16:         while (true)

  17:         {

  18:             string input = Console.ReadLine();

  19:             if (input.Trim().ToLower() == "s")

  20:             {

  21:                 Common.ConsoleWriteLine("线程 暂停 运行.");

  22:                 resetEventHandler.Reset();

  23:             }

  24:             else if (input.Trim().ToLower() == "r")

  25:             {

  26:                 Common.ConsoleWriteLine("线程 继续 运行.");

  27:                 resetEventHandler.Set();

  28:             }

  29:         }

  30:  

  31:     }

  32: }

  33: class Common

  34: {

  35:     public static void CreateThreads(Action runFunc, int tCount = 2)

  36:     {

  37:         var i = 0;

  38:         while (i < tCount)

  39:         {

  40:             var t = new Thread(new ThreadStart(runFunc));

  41:             t.Start();

  42:             i++;

  43:         }

  44:     }

  45:     public static void ConsoleWriteLine(string msg, bool withThreadSign = true, bool withDateTimeSign = false)

  46:     {

  47:         Console.WriteLine(

  48:             msg +

  49:             (withThreadSign ? (" [ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString() + " ]") : "") +

  50:             (withDateTimeSign ? ("[" + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff") + "]") : "")

  51:             );

  52:     }

  53: }

ResetEventHandle.cs
   1: class ResetEventHandle<T> where T : EventWaitHandle

   2: {

   3:     /// <summary>test

   4:     /// 通知一个或多个正在等待的线程已发生事件,处理器类型。      “等待线程 事件通知 处理器”.

   5:     /// ManualResetEvent 继承自 ,EventWaitHandle:WaitHandle , 表示一个线程同步事件。 

   6:     /// EventWaitHandle 主要操作方法: Set() , Reset()。

   7:     /// WaitHandle 封装等待对共享资源的独占访问的操作系统特定的对象

   8:     /// </summary>

   9:     private T _resetEventHandler;

  10:  

  11:     public ResetEventHandle(T resetEventHandler)

  12:     {

  13:         // 初始化一个  “等待线程 事件通知 处理器” 。

  14:         // 参数 initialState ,初始状态是否为 终止:

  15:         //     true   终止  ,即目前已无事件发生,无需等待程暂,线程继续运行

  16:         //     false  非终止,即目前有事件发生,需等待,线停运行

  17:         // this._mre = new ManualResetEvent(true);

  18:         this._resetEventHandler = resetEventHandler;

  19:     }

  20:  

  21:     public string CurTypeName { get { return _resetEventHandler.GetType().Name; } }

  22:     /// <summary>

  23:     /// 将事件状态设置为终止状态, 即目前已无事件发生,无需等待,其他等待中的线程继续运行

  24:     /// </summary>

  25:     public void Set() { this._resetEventHandler.Set(); }

  26:  

  27:     /// <summary>

  28:     /// 将事件状态设置为非终止状态,即目前有事件发生,需等待,其他线程暂停运行

  29:     /// </summary>

  30:     public void Reset() { this._resetEventHandler.Reset(); }

  31:  

  32:     public void Run()

  33:     {

  34:         string strThreadID = string.Empty;

  35:         try

  36:         {

  37:             while (true)

  38:             {

  39:                 // 阻塞当前线程 ,直到 _resetEventHandler 收到新的事件信号(即 set or reset)

  40:                 this._resetEventHandler.WaitOne();

  41:  

  42:                 strThreadID = Thread.CurrentThread.ManagedThreadId.ToString();

  43:                 Common.ConsoleWriteLine("线程 (" + strThreadID + ") 正在运行.");

  44:  

  45:                 Thread.Sleep(5000);

  46:             }

  47:         }

  48:         catch (Exception ex)

  49:         {

  50:             Common.ConsoleWriteLine("线程 (" + strThreadID + ") 异常!错误:" + ex.Message.ToString());

  51:         }

  52:     }

  53: }

在Program.Main()中,如果调用AutoResetEvent类型,我们每次输入r,创建的三个线程继续运行,但一次只运行一个线程,其他被阻塞的线程则继续等待通知信号:

211. Orchard学习 二 2、ManualResetEvent 与 AutoResetEvent

如果调用ManualResetEvent类型,我们每次输入r,创建的三个线程继续运行,每次所有创建后被阻塞的线程都会运行起来:

211. Orchard学习 二 2、ManualResetEvent 与 AutoResetEvent

三、生活中的例子:
参考文档中,所列公路收费站的例子并不恰当。我们参考一下地铁闸机:

211. Orchard学习 二 2、ManualResetEvent 与 AutoResetEvent

四、一点总结:

    • ManualResetEvent 和 AutoResetEvent都可以继续运行或阻塞线程,并且都是一次性阻塞所有影响到的线程;
    • ManualResetEvent 是一次继续运行一批线程;
    • AutoResetEvent 是一次继续运行一个线程,其他影响到的线程继续等待新的事件通知信号;
    • AutoResetEvent.Set() = ManualResetEvent.Set() + ManualResetEvent.Reset();
    • 如果共享资源仅允许单线程使用的情况下,应选择AutoResetEvent;如果共享资源允许多个线程同时使用,则可以选择ManualResetEvent;
    • 言外之意,只有一个待处理线程时,ManualResetEvent 和 AutoResetEvent效果是一致的。

DEMO代码

https://bitbucket.org/wsliujun/700-01-consoleapp_resetevent/

相关参考

http://www.cnblogs.com/tianzhiliang/archive/2011/03/04/1970726.html

http://www.cnblogs.com/qingyun163/archive/2013/01/05/2846633.html

http://msdn.microsoft.com/zh-cn/library/system.threading.manualresetevent.aspx

http://msdn.microsoft.com/zh-cn/library/system.threading.autoresetevent.aspx

211. Orchard学习 二 2、ManualResetEvent 与 AutoResetEvent的更多相关文章

  1. C&num;多线程之二:ManualResetEvent和AutoResetEvent

    初次体验 ManualResetEvent和AutoResetEvent主要负责多线程编程中的线程同步:以下一段是引述网上和MSDN的解析: 在.Net多线程编程中,AutoResetEvent和Ma ...

  2. 213&period; Orchard学习 二 3、001&period;IOrchardHost 与Autofac

    继前篇,在Orchard Application_Start() -> HostInitialization() 里,调用 OrchardStarter.CreateHost创建IOrchard ...

  3. 200&period; Orchard学习 目录

    201. Orchard学习 一.基础 210. Orchard学习 二.启动 211. Orchard学习 二 1.Application_Start 212. Orchard学习 二 2.Manu ...

  4. C&num; 多线程学习系列四之ThreadPool取消、超时子线程操作以及ManualResetEvent和AutoResetEvent信号量的使用

    1.简介 虽然ThreadPool.Thread能开启子线程将一些任务交给子线程去承担,但是很多时候,因为某种原因,比如子线程发生异常.或者子线程的业务逻辑不符合我们的预期,那么这个时候我们必须关闭它 ...

  5. emberjs学习二&lpar;ember-data和localstorage&lowbar;adapter&rpar;

    emberjs学习二(ember-data和localstorage_adapter) 准备工作 首先我们加入ember-data和ember-localstorage-adapter两个依赖项,使用 ...

  6. 【C&num;】【Thread】ManualResetEvent和AutoResetEvent区别

    ManualResetEvent和AutoResetEvent主要用于线程之间同步问题. 主要使用方法有Set();Reset();WaitOne(); Set():将事件状态设置为终止状态,允许一个 ...

  7. ReactJS入门学习二

    ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...

  8. ManualResetEvent和AutoResetEvent的区别实例

    ManualResetEvent和AutoResetEvent的作用可以理解为在线程执行中插入停顿点flag终止程序运行,然后通过设置flag的状态来使得程序继续运行. 两者的区别是:ManualRe ...

  9. TweenMax动画库学习&lpar;二&rpar;

    目录            TweenMax动画库学习(一)            TweenMax动画库学习(二)            TweenMax动画库学习(三)            Tw ...

随机推荐

  1. TCP的那些事儿(上)

    TCP的那些事儿(上) 原文链接:http://coolshell.cn/articles/11564.html TCP是一个巨复杂的协议,因为他要解决很多问题,而这些问题又带出了很多子问题和阴暗面. ...

  2. 『安全工具』Nessus多功能扫描器

    0x 00 前言 写这篇博客,证明我还活着…… 0x 01 安装Nessus  直接官网  www.tenable.com/products/nessus/select-your-operating- ...

  3. 可以供MFC调用的,QT实现的DLL(使用qt-solutions的qtwinmigrate实现)

    MFC和QT的消息循环机制不同,所以,要让QT写的DLL可以供MFC调用,要做一点特殊的处理 #include <qmfcapp.h> #include <qwinwidget.h& ...

  4. struts2 &lt&semi;s&colon;iterator&sol;&gt&semi;怎样取得循环的索引

    <s:iterator value="list" id="user" status="L"> <s:property va ...

  5. hdu1042

    #include"stdio.h" #include"stdlib.h" #include"string.h" #define N 1000 ...

  6. ArcGis dbf读写——挂接Excel到属性表 C&num;

    ArcMap提供了挂接Excel表格信息到属性表的功能,但是当数据量较大到以万计甚至十万计的时候这个功能就歇菜了,当然,你可以考虑分段挂接.这个挂接功能只是做了一个表关联,属性记录每个字段的信息需要通 ...

  7. python--第四天总结

    lambda表达式 处理简单函数自动返回 学习条件运算时,对于简单的 if else 语句,可以使用三元运算来表示,即: # 普通条件语句 if 1 == 1: name = 'wupeiqi' el ...

  8. (原创)一个简洁通用的调用DLL函数的帮助类

    本次介绍一种调用dll函数的通用简洁的方法,消除了原来调用方式的重复与繁琐,使得我们调用dll函数的方式更加方便简洁.用过dll的人会发现c++中调用dll中的函数有点繁琐,调用过程是这样的:在加载d ...

  9. js this问题和es6箭头函数this问题

    JS中this的四种用法 1.在一般函数方法中使用 this 指代全局对象 function test(){ this.x = 1; alert(this.x); } test(); //1 2.作为 ...

  10. 20155314 2016-2017-2 《Java程序设计》第1周学习总结

    20155314 2016-2017-2 <Java程序设计>第1周学习总结 学习目标 了解Java基础知识(已完成) 了解JVM.JRE与JDK,并下载.安装.测试JDK(已完成) 了解 ...