一、Orchard里异步请求处理线程队列的控制
Orchard的Orchard.WarmupStarter模块,为HttpApplication.BeginRequest时间附加了一个异步处理事件:BeginBeginRequest。
1: ///2: /// 启动 System.Web.HttpApplication.BeginRequest 的异步处理的 System.Web.BeginEventHandler3: /// System.Web.HttpApplication.BeginRequest 在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生。4: ///5: private IAsyncResult BeginBeginRequest(object sender, EventArgs e, AsyncCallback cb, object extradata)6: {7: // host is available, process every requests, or file is processed8: 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: else19: {20: // this is the "on hold" execution path21: var asyncResult = new WarmupAsyncResult(cb, extradata);22: Await(asyncResult.Completed);23: return asyncResult;24: }25: }
在请求开始时,检查系统状态,“不在预热中”或者找到了与请求的URL匹配的静态文件资源,则构造一个 DoneAsncResult类型实例,执行并返回结果状态。如果正在预热,或没有匹配的静态文件资源,将请求加入一个待执行队列,直到预热完成发出信号后再执行(这个见Orchard.WarmupStarter.Starter.LaunchStartupThread())。
我们再来看 DoneAsyncResult和WarmupAsyncResult
1: ///2: /// AsyncResult for "on hold" request (resumes when "Completed()" is called)3: ///4: private class WarmupAsyncResult : IAsyncResult5: {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: ///23: /// 已 “执行完成 or 正在处理” 的异步操作状态24: /// Async result for "ok to process now" requests25: ///26: private class DoneAsyncResult : IAsyncResult27: {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 都继承自 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 Program2: {3: static void Main(string[] args)4: {5: Common.ConsoleWriteLine("----------------------", false);6: Common.ConsoleWriteLine(" S 暂停 R 继续 ", false);7:8: var resetEventHandler = new ResetEventHandle(new ManualResetEvent(true)); 9: //var resetEventHandler = new ResetEventHandle(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 Common34: {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.cs1: class ResetEventHandlewhere T : EventWaitHandle 2: {3: ///test 4: /// 通知一个或多个正在等待的线程已发生事件,处理器类型。 “等待线程 事件通知 处理器”.5: /// ManualResetEvent 继承自 ,EventWaitHandle:WaitHandle , 表示一个线程同步事件。6: /// EventWaitHandle 主要操作方法: Set() , Reset()。7: /// WaitHandle 封装等待对共享资源的独占访问的操作系统特定的对象8: ///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: ///23: /// 将事件状态设置为终止状态, 即目前已无事件发生,无需等待,其他等待中的线程继续运行24: ///25: public void Set() { this._resetEventHandler.Set(); }26:27: ///28: /// 将事件状态设置为非终止状态,即目前有事件发生,需等待,其他线程暂停运行29: ///30: public void Reset() { this._resetEventHandler.Reset(); }31:32: public void Run()33: {34: string strThreadID = string.Empty;35: try36: {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,创建的三个线程继续运行,但一次只运行一个线程,其他被阻塞的线程则继续等待通知信号:
如果调用ManualResetEvent类型,我们每次输入r,创建的三个线程继续运行,每次所有创建后被阻塞的线程都会运行起来:
三、生活中的例子:
参考文档中,所列公路收费站的例子并不恰当。我们参考一下地铁闸机:
四、一点总结:
-
- ManualResetEvent 和 AutoResetEvent都可以继续运行或阻塞线程,并且都是一次性阻塞所有影响到的线程;
- ManualResetEvent 是一次继续运行一批线程;
- AutoResetEvent 是一次继续运行一个线程,其他影响到的线程继续等待新的事件通知信号;
- AutoResetEvent.Set() = ManualResetEvent.Set() + ManualResetEvent.Reset();
- 如果共享资源仅允许单线程使用的情况下,应选择AutoResetEvent;如果共享资源允许多个线程同时使用,则可以选择ManualResetEvent;
- 言外之意,只有一个待处理线程时,ManualResetEvent 和 AutoResetEvent效果是一致的。
DEMO代码
相关参考