Delphi Event Bus进阶(一)控制订阅方法的线程模式
上文根据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)一个消息,你要清楚的知道接收者是在主线程还是在子线程中执行,或者说是同步执行还是异步执行!