上文根據Delphi Event Bus開源項目自帶的例子,對其基本用法做了介紹,相信通過這個例子,你也能明白如何使用Delphi Event Bus到自己的項目中,讓項目代碼更解耦,易維護與易擴展。
今天,進一步說說如何更深入的使用Delphi Event Bus。
首先,想說的是對於訂閱方法的注解,如何定義當他執行時,用什么樣的線程。
當我們用[Subscribe]定義一個訂閱方法時,這個方法是在主線程中執行,還是在單獨的線程中執行?這一點對於我們來說,是必須要清晰的。看下面的代碼:
[Subscribe] procedure ReceiveFrameNotification(AEvent:IFrameNotifyMainEvent);
這里,我們要知道[Subscribe]還能用TThreadMode進一步注解,等明白了如何使用TThreadMode,才能正確回答上面的問題。接下來,看一下TThreadMode的定義:
/// <summary> /// Thead mode of the subscriber method. /// 訂閱方法的線程模式 /// </summary> TThreadMode = ( /// <summary> /// The subscriber method will be invoked in the same posting thread where /// IEventBus.Post is called. /// 訂閱方法將在IEventBus.Post時的線程中執行,如果在主線程中Post,那么訂閱方法就在主線程中執行,如果在子線程中執行,那么訂閱方法就在這個子線程中執行。 /// </summary> Posting, /// <summary> /// The subscriber method will be invoked in the main thread. ///訂閱方法在主線程中執行 /// </summary> Main, /// <summary> /// The subscriber method will be invoked asynchronously in a new thread /// other than the posting thread. ///訂閱方法將在新的子線程中執行 /// </summary> Async, /// <summary> /// If the posting thread is the main thread, the subscriber method will /// be invoked asynchronously in a new thread other than the posting /// thread. If the posting thread is NOT the main thread, the subscriber /// method will be invoked synchronously in the same posting thread. ///保證在子線程中執行,如果在主線程中Post,那么訂閱方法將在一個新的子線程中執行,如果Post在子線程中調用,那么訂閱方法將和Post所在的線程中執行。 /// </summary> Background );
根據作者的源代碼中注釋做了翻譯,默認情況下,[Subscribe]線程模式為TThread.Posting,也就是在post時所在的線程中執行。現在你可以回答上面的問題了。那如何定義[Subscribe]的線程模式呢?象下面這樣,讓ReceiveFrameNotification在主線程中執行:
[Subscribe(TThreadMode.Main)] procedure ReceiveFrameNotification(AEvent:IFrameNotifyMainEvent);
出差路上,要下車了,現到這里。
繼續補充上面的內容,也是經驗之談,走過的彎路。
如果對上面的說法你還是不清楚,那參考源碼能有助於理解,當你Post完消息后,是執行這個方法TEventBus.PostToSubscription,在這個方法中執行具體的訂閱方法。
procedure TEventBus.PostToSubscription(ASubscription: TSubscription; const AEvent: IInterface; AIsMainThread: Boolean); var LProc: TProc; begin LProc := procedure begin InvokeSubscriber(ASubscription, [AEvent as TObject]); end;//根據ASubscription取得訂閱方法 case ASubscription.SubscriberMethod.ThreadMode of//根據訂閱方法設置的線程類型來執行訂閱方法 Posting: LProc(); Main: if (AIsMainThread) then LProc() else TThread.Queue(nil, TThreadProcedure(LProc)); Background: if (AIsMainThread) then TTask.Run(LProc) else LProc(); Async: TTask.Run(LProc); else raise Exception.Create('Unknown thread mode'); end; end;
在實際項目中,我們要怎么控制線程模式呢?原則就是你要保證,你自己清楚的知道,發布一個消息時,即執行Post時,收到消息的訂閱方法是在主線程中執行還是在子線程中執行,說到這里,我最討厭的就是只使用[Subscribe],而不是明確的定義一個訂閱方法的線程模式。換句話說,我們不要輕易使用默認值TThreadMode.Posting來定義。確切的說,要么用TThreadMode.Main來定義一個訂閱方法在主線程中執行,要么用TThreadMode.ASync定義一個訂閱方法在子線程中執行,即異步執行。
重復代表重要:當你發布(Post)一個消息,你要清楚的知道接收者是在主線程還是在子線程中執行,或者說是同步執行還是異步執行!