红鱼儿

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)一个消息,你要清楚的知道接收者是在主线程还是在子线程中执行,或者说是同步执行还是异步执行!

 

posted on 2021-03-15 18:13  红鱼儿  阅读(767)  评论(0编辑  收藏  举报