上一篇講了事件,以及爲何要使用事件,主要是爲了解耦,可是有同窗就問了,同步若是訂閱事件的人太多,好比13億人都關心上頭條的事,那麼RaiseEvent得等13億人都處理完,那得多久呀,今後不再敢發事件了。
舉個例子,你在網上下單,下完單要通知庫房,甚至要通知供應商補貨,若是都是同步的話,消費者還不等急死呀,實際上你在電商網站上下個單, 通常你很快就能到訂單頁面,那個頁面告訴你:「兄弟,訂單已經建立成功,訂單號是xxxxx-xxxxx-xxxx-xxxx,你的訂單已經提交到庫房」 等。而後你就很快了的下另外一單了。好吧,
提問的同窗,說好的妹子呢?異步
咱們先回顧一下同步事件驅動的代碼性能
public void Head() { var NewsPaper = new NewsPaper("南都娛樂"); NewsPaper.WriteToHeader("汪峯"); RaiseEvent(new HeadedEvent {Name = "汪峯"}); } private void RaiseEvent(HeadedEvent headedEvent) { EventBus.Publish<HeadedEvent>(new HeadedEvent { Name = "汪峯" }); }
public interface IEventHandler<TEvent> where TEvent : Event { void Handle(TEvent e); } public class HeadedEvent:Event { public string Name { get; set; } } public class GuoJiZhangMotherEventHandler : IEventHandler<HeadedEvent> { public void Handle(HeadedEvent e) { Console.WriteLine(e.Name+", Are you kidding me?"); } } public class PiMingEventHandler:IEventHandler<HeadedEvent> { public void Handle(HeadedEvent e) { Console.WriteLine(e.Name+", Guo Ji Zhang is your last wife?"); } }
咱們能夠看到正真的事件協調者是EventBus, 以前的代碼以下是同步的。網站
public class EventBus { public static void Publish<T>(T concreteEvent) where T: Event { var handlers = _container.ResolveAll<IEventHandler<T>>(); foreach (var handle in handlers) { handle.Handle(concreteEvent); } } }
爲了提升性能,咱們能夠先來第一步改進this
public void Publish<T>(T @event) where T : Event { var handlers = _eventHandlerFactory.GetHandlers<T>(); handlers.AsParallel().ForAll((h)=> h.Handle(@event)); }
咱們能夠看到,如今並行處理能夠大大加快速度,可是有兩個問題,第一個問題就是沒有處理異常,因此讓咱們加上異常。code
public void Publish<T>(T @event) where T : Event { var handlers = _eventHandlerFactory.GetHandlers<T>(); handlers.AsParallel().ForAll((h)=> HandleEvent<T>(h,@event)); } private void HandleEvent<T>(IEventHandler<T> handle, T @event) where T : Event { try { handle.Handle(@event); } catch (Exception e) { // Log the exception, as the caller don't care this } } }
第二個問題,就是咱們雖然用了並行加快了速度,可是尚未正真實現異步,整個程序仍是等全部Handler處理完才返回。事件
public void Publish<T>(T @event) where T : Event { var handlers = _eventHandlerFactory.GetHandlers<T>(); handlers.Select(h => Task.Factory.StartNew(() => HandleEvent<T>(h, @event))); }
這段代碼執行完,盡然發現Handler沒有執行,好吧,緣由是IQueryable的延遲執行,因此咱們須要調用一下ToListget
public void Publish<T>(T @event) where T : Event { var handlers = _eventHandlerFactory.GetHandlers<T>(); handlers.Select(h => Task.Factory.StartNew(() => HandleEvent<T>(h, @event))).ToArray(); }
好了,咱們就這樣輕易的實現了一個AsyncEventBus, 是否是感謝.Net的強大?同步
這裏還只是一個系統內部的Async, 若是涉及到系統之間的交互,這個就不行了,並且若是異步處理有錯誤,咱們就會有信息丟失,因此須要更健壯的異步事件處理系統,這個後面再講,可是通常的系統咱們只須要把出錯的事件記錄下來,而後再看要不要處理就能夠。string
另外,異步要處理的東西不少,好比處理完畢後,如何通知用戶,仍是讓用戶刷新? 我我的建議,通常狀況下都不要用異步,只有在真的須要的時候再用。it