鴻蒙OS 線程管理開發(fā)指導(dǎo)

2020-09-18 11:39 更新

場景介紹

如果應(yīng)用的業(yè)務(wù)邏輯比較復(fù)雜,可能需要創(chuàng)建多個線程來執(zhí)行多個任務(wù)。這種情況下,代碼復(fù)雜難以維護,任務(wù)與線程的交互也會更加繁雜。要解決此問題,開發(fā)者可以使用“TaskDispatcher”來分發(fā)不同的任務(wù)。

接口說明

TaskDispatcher 是一個任務(wù)分發(fā)器,它是 Ability 分發(fā)任務(wù)的基本接口,隱藏任務(wù)所在線程的實現(xiàn)細節(jié)。

為保證應(yīng)用有更好的響應(yīng)性,我們需要設(shè)計任務(wù)的優(yōu)先級。在 UI 線程上運行的任務(wù)默認以高優(yōu)先級運行,如果某個任務(wù)無需等待結(jié)果,則可以用低優(yōu)先級。

優(yōu)先級 詳細描述
HIGH 最高任務(wù)優(yōu)先級,比默認優(yōu)先級、低優(yōu)先級的任務(wù)有更高的幾率得到執(zhí)行。
DEFAULT 默認任務(wù)優(yōu)先級, 比低優(yōu)先級的任務(wù)有更高的幾率得到執(zhí)行。
LOW 低任務(wù)優(yōu)先級,比高優(yōu)先級、默認優(yōu)先級的任務(wù)有更低的幾率得到執(zhí)行。

TaskDispatcher 具有多種實現(xiàn),每種實現(xiàn)對應(yīng)不同的任務(wù)分發(fā)器。在分發(fā)任務(wù)時可以指定任務(wù)的優(yōu)先級,由同一個任務(wù)分發(fā)器分發(fā)出的任務(wù)具有相同的優(yōu)先級。系統(tǒng)提供的任務(wù)分發(fā)器有 GlobalTaskDispatcher、ParallelTaskDispatcher、SerialTaskDispatcher 、SpecTaskDispatcher。

  • GlobalTaskDispatcher

全局并發(fā)任務(wù)分發(fā)器,由 Ability 執(zhí)行 getGlobalTaskDispatcher()獲取。適用于任務(wù)之間沒有聯(lián)系的情況。一個應(yīng)用只有一個 GlobalTaskDispatcher,它在程序結(jié)束時才被銷毀。

  TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT);

  • ParallelTaskDispatcher

并發(fā)任務(wù)分發(fā)器,由 Ability 執(zhí)行 createParallelTaskDispatcher() 創(chuàng)建并返回。與 GlobalTaskDispatcher 不同的是,ParallelTaskDispatcher 不具有全局唯一性,可以創(chuàng)建多個。開發(fā)者在創(chuàng)建或銷毀 dispatcher 時,需要持有對應(yīng)的對象引用 。

  String dispatcherName = "parallelTaskDispatcher";
  TaskDispatcher parallelTaskDispatcher = createParallelTaskDispatcher(dispatcherName, TaskPriority.DEFAULT);

  • SerialTaskDispatcher

串行任務(wù)分發(fā)器,由 Ability 執(zhí)行 createSerialTaskDispatcher()創(chuàng)建并返回。由該分發(fā)器分發(fā)的所有的任務(wù)都是按順序執(zhí)行,但是執(zhí)行這些任務(wù)的線程并不是固定的。如果要執(zhí)行并行任務(wù),應(yīng)使用 ParallelTaskDispatcher 或者 GlobalTaskDispatcher,而不是創(chuàng)建多個 SerialTaskDispatcher。如果任務(wù)之間沒有依賴,應(yīng)使用GlobalTaskDispatcher 來實現(xiàn)。它的創(chuàng)建和銷毀由開發(fā)者自己管理,開發(fā)者在使用期間需要持有該對象引用。

  String dispatcherName = "serialTaskDispatcher";
  TaskDispatcher serialTaskDispatcher = createSerialTaskDispatcher(dispatcherName, TaskPriority.DEFAULT);

  • SpecTaskDispatcher

專有任務(wù)分發(fā)器,綁定到專有線程上的任務(wù)分發(fā)器。目前已有的專有線程是主線程。 UITaskDispatcher 和 MainTaskDispatcher 都屬于 SpecTaskDispatcher。建議使用 UITaskDispatcher。

UITaskDispatcher:綁定到應(yīng)用主線程的專有任務(wù)分發(fā)器, 由 Ability 執(zhí)行 getUITaskDispatcher() 創(chuàng)建并返回。 由該分發(fā)器分發(fā)的所有的任務(wù)都是在主線程上按順序執(zhí)行,它在應(yīng)用程序結(jié)束時被銷毀。

  TaskDispatcher uiTaskDispatcher = getUITaskDispatcher();

MainTaskDispatcher:由 Ability 執(zhí)行 getMainTaskDispatcher() 創(chuàng)建并返回。

  TaskDispatcher mainTaskDispatcher= getMainTaskDispatcher()

開發(fā)步驟

  • syncDispatch

同步派發(fā)任務(wù):派發(fā)任務(wù)并在當前線程等待任務(wù)執(zhí)行完成。在返回前,當前線程會被阻塞。

如下代碼示例展示了如何使用 GlobalTaskDispatcher 派發(fā)同步任務(wù):

  globalTaskDispatcher.syncDispatch(new Runnable() {
              @Override
              public void run() {
                  HiLog.info(label, "sync task1 run");
              }
          });
  HiLog.info(label, "after sync task1");

   
  globalTaskDispatcher.syncDispatch(new Runnable() {
              @Override
              public void run() {
                  HiLog.info(label, "sync task2 run");
              }
          });
  HiLog.info(label, "after sync task2");

   
  globalTaskDispatcher.syncDispatch(new Runnable() {
              @Override
              public void run() {
                  HiLog.info(label, "sync task3 run");
              }
          });
  HiLog.info(label, "after sync task3");

   
  // 執(zhí)行結(jié)果如下:
  // sync task1 run
  // after sync task1
  // sync task2 run
  // after sync task2
  // sync task3 run
  // after sync task3

說明

如果對 syncDispatch 使用不當, 將會導(dǎo)致死鎖。如下情形可能導(dǎo)致死鎖發(fā)生:

  • 在專有線程上,利用該專有任務(wù)分發(fā)器進行 syncDispatch。
  • 在被某個串行任務(wù)分發(fā)器(dispatcher_a)派發(fā)的任務(wù)中,再次利用同一個串行任務(wù)分發(fā)器(dispatcher_a)對象派發(fā)任務(wù)。
  • 在被某個串行任務(wù)分發(fā)器(dispatcher_a)派發(fā)的任務(wù)中,經(jīng)過數(shù)次派發(fā)任務(wù),最終又利用該(dispatcher_a)串行任務(wù)分發(fā)器派發(fā)任務(wù)。例如:dispatcher_a 派發(fā)的任務(wù)使用 dispatcher_b 進行任務(wù)的派發(fā),在 dispatcher_b 派發(fā)的任務(wù)中又利用 dispatcher_a 進行派發(fā)任務(wù)。
  • 串行任務(wù)分發(fā)器(dispatcher_a)派發(fā)的任務(wù)中利用串行任務(wù)分發(fā)器(dispatcher_b)進行同步派發(fā)任務(wù),同時 dispatcher_b 派發(fā)的任務(wù)中利用串行任務(wù)分發(fā)器(dispatcher_a)進行同步派發(fā)任務(wù)。在特定的線程執(zhí)行順序下將導(dǎo)致死鎖。

  • asyncDispatch

異步派發(fā)任務(wù):派發(fā)任務(wù),并立即返回,返回值是一個可用于取消任務(wù)的接口。

如下代碼示例展示了如何使用 GlobalTaskDispatcher 派發(fā)異步任務(wù):

  Revocable revocable =  globalTaskDispatcher.asyncDispatch(new Runnable() {
          @Override
              public void run() {
                  HiLog.info(label, "async task1 run");
              }
          });
  HiLog.info(label, "after async task1");

   
  // 執(zhí)行結(jié)果可能如下:
  // after async task1
  // async task1 run

  • delayDispatch

異步延遲派發(fā)任務(wù):異步執(zhí)行,函數(shù)立即返回,內(nèi)部會在延時指定時間后將任務(wù)派發(fā)到相應(yīng)隊列中。延時時間參數(shù)僅代表在這段時間以后任務(wù)分發(fā)器會將任務(wù)加入到隊列中,任務(wù)的實際執(zhí)行時間可能晚于這個時間。具體比這個數(shù)值晚多久,取決于隊列及內(nèi)部線程池的繁忙情況。

如下代碼示例展示了如何使用 GlobalTaskDispatcher 延遲派發(fā)任務(wù):

   final long callTime = System.currentTimeMillis();
   final long delayTime = 50;
   Revocable revocable =  globalTaskDispatcher.delayDispatch(new Runnable() {
              @Override
              public void run() {
                  HiLog.info(label, "delayDispatch task1 run");
                  final long actualDelayMs = System.currentTimeMillis() - callTime;
                  HiLog.info(label, "actualDelayTime >= delayTime : %{public}b" + (actualDelayMs >= delayTime));
              }
          }, delayTime );
   HiLog.info(label, "after delayDispatch task1");

   
  // 執(zhí)行結(jié)果可能如下:
  // after delayDispatch task1
  // delayDispatch task1 run
  // actualDelayTime >= delayTime : true

  • Group

任務(wù)組:表示一組任務(wù),且該組任務(wù)之間有一定的聯(lián)系,由 TaskDispatcher 執(zhí)行 createDispatchGroup 創(chuàng)建并返回。將任務(wù)加入任務(wù)組,返回一個用于取消任務(wù)的接口。

如下代碼示例展示了任務(wù)組的使用方式:將一系列相關(guān)聯(lián)的下載任務(wù)放入一個任務(wù)組,執(zhí)行完下載任務(wù)后關(guān)閉應(yīng)用。

  void groupTest(Context context) {
      TaskDispatcher dispatcher = context.createParallelTaskDispatcher(dispatcherName, TaskPriority.DEFAULT);
      // 創(chuàng)建任務(wù)組。
      Group group = dispatcher.createDispatchGroup();
      // 將任務(wù)1加入任務(wù)組,返回一個用于取消任務(wù)的接口。
      dispatcher.asyncGroupDispatch(group, new Runnable(){
          public void run() {
              HiLog.info(label, "download task1 is running");
              downLoadRes(url1);
          }
      });
     // 將與任務(wù)1相關(guān)聯(lián)的任務(wù)2加入任務(wù)組。
      dispatcher.asyncGroupDispatch(group, new Runnable(){
          public void run() {
              HiLog.info(label, "download task2 is running");
              downLoadRes(url2);
          }
      });
      // 在任務(wù)組中的所有任務(wù)執(zhí)行完成后執(zhí)行指定任務(wù)。
      dispatcher.groupDispatchNotify(group, new Runnable(){
          public void run() {
              HiLog.info(label, "the close task is running after all tasks in the group are completed");
              closeApp();
          }
      });
  }
  ?
  // 可能的執(zhí)行結(jié)果:
  // download task1 is running
  // download task2 is running
  // the close task is running after all tasks in the group are completed

   
  // 另外一種可能的執(zhí)行結(jié)果:
  // download task2 is running
  // download task1 is running
  // the close task is running after all tasks in the group are completed

  • Revocable

取消任務(wù):Revocable 是取消一個異步任務(wù)的接口。異步任務(wù)包括通過 asyncDispatch、delayDispatch、asyncGroupDispatch 派發(fā)的任務(wù)。如果任務(wù)已經(jīng)在執(zhí)行中或執(zhí)行完成,則會返回取消失敗。

如下代碼示例展示了如何取消一個異步延時任務(wù):

  void postTaskAndRevoke(Context context) {
      TaskDispatcher dispatcher = context.getUITaskDispatcher();
      Revocable revocable = dispatcher.delayDispatch(new Runnable(){
           HiLog.info(label, "delay dispatch");
      }, 10);
      boolean revoked = revocable.revoke();
      HiLog.info(label, "%{public}b", revoked);
  }
  ?
  // 一種可能的結(jié)果如下 :
  // true

  • syncDispatchBarrier

同步設(shè)置屏障任務(wù):在任務(wù)組上設(shè)立任務(wù)執(zhí)行屏障,同步等待任務(wù)組中的所有任務(wù)執(zhí)行完成,再執(zhí)行指定任務(wù)。

說明

在全局并發(fā)任務(wù)分發(fā)器(GlobalTaskDispatcher)上同步設(shè)置任務(wù)屏障,將不會起到屏障作用。

如下代碼示例展示了如何同步設(shè)置屏障:

      TaskDispatcher dispatcher = context.createParallelTaskDispatcher(dispatcherName, TaskPriority.DEFAULT);
      // 創(chuàng)建任務(wù)組。
      Group group = dispatcher.createDispatchGroup();
      // 將任務(wù)加入任務(wù)組,返回一個用于取消任務(wù)的接口。
      dispatcher.asyncGroupDispatch(group, new Runnable(){
          public void run() {
              HiLog.info(label, "task1 is running");  // 1
          }
      });
      dispatcher.asyncGroupDispatch(group, new Runnable(){
          public void run() {
              HiLog.info(label, "task2 is running");  // 2
          }
      });

      
      dispatcher.syncDispatchBarrier(new Runnable() {  
      public void run() {  
          HiLog.info(label, "barrier");  // 3
      }});  
      HiLog.info(label, "after syncDispatchBarrier");  // 4
  }
  ?
  // 1和2的執(zhí)行順序不定;3和4總是在1和2之后按順序執(zhí)行。

   
  // 可能的執(zhí)行結(jié)果:
  // task1 is running
  // task2 is running
  // barrier
  // after syncDispatchBarrier

   
  // 另外一種執(zhí)行結(jié)果:
  // task2 is running
  // task1 is running
  // barrier
  // after syncDispatchBarrier

  • asyncDispatchBarrier

異步設(shè)置屏障任務(wù):在任務(wù)組上設(shè)立任務(wù)執(zhí)行屏障后直接返回,指定任務(wù)將在任務(wù)組中的所有任務(wù)執(zhí)行完成后再執(zhí)行。

說明

在全局并發(fā)任務(wù)分發(fā)器(GlobalTaskDispatcher)上異步設(shè)置任務(wù)屏障,將不會起到屏障作用??梢允褂貌l(fā)任務(wù)分發(fā)器(ParallelTaskDispatcher)分離不同的任務(wù)組,達到微觀并行、宏觀串行的行為。

如下代碼示例展示了如何異步設(shè)置屏障:

      TaskDispatcher dispatcher = context.createParallelTaskDispatcher(dispatcherName, TaskPriority.DEFAULT);
      // 創(chuàng)建任務(wù)組。
      Group group = dispatcher.createDispatchGroup();
      // 將任務(wù)加入任務(wù)組,返回一個用于取消任務(wù)的接口。
      dispatcher.asyncGroupDispatch(group, new Runnable(){
          public void run() {
              HiLog.info(label, "task1 is running");  // 1
          }
      });
      dispatcher.asyncGroupDispatch(group, new Runnable(){
          public void run() {
              HiLog.info(label, "task2 is running");  // 2
          }
      });

      
      dispatcher.asyncDispatchBarrier(new Runnable() {  
          public void run() {  
              HiLog.info(label, "barrier");  // 3
          }
  });  
      HiLog.info(label, "after syncDispatchBarrier");  // 4
  }
  ?
  // 1和2的執(zhí)行順序不定,但總在3和4之前執(zhí)行;4可能在3之前執(zhí)行

   
  // 可能的執(zhí)行結(jié)果:
  // task1 is running
  // task2 is running
  // after syncDispatchBarrier
  // barrier 

  • applyDispatch

執(zhí)行多次任務(wù):對指定任務(wù)執(zhí)行多次。

如下代碼示例展示了如何執(zhí)行多次任務(wù):

   final int total = 10;
   final CountDownLatch latch = new CountDownLatch(total);
   final ArrayList<Long> indexList = new ArrayList<>(total);

   
   // 執(zhí)行任務(wù) total 次
   dispatcher.applyDispatch((index) -> {
       indexList.add(index);
       latch.countDown();
   }, total);

   
   // 設(shè)置任務(wù)超時
   try {    
       latch.await();
   } catch (InterruptedException exception) {
      HiLog.info(label, "latch exception");
   }
   HiLog.info(label, "list size matches, %{public}b", (total == indexList.size()));

   
  // 執(zhí)行結(jié)果:
  // list size matches, true
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號