HarmonyOS 基礎之線程管理
概述
不同應用在各自獨立的進程中運行。當應用以任何形式啟動時,系統為其創建進程,該進程將持續運行。當進程完成當前任務處于等待狀態,且系統資源不足時,系統自動回收。
在啟動應用時,系統會為該應用創建一個稱為“主線程”的執行線程。該線程隨著應用創建或消失,是應用的核心線程。UI界面的顯示和更新等操作,都是在主線程上進行。主線程又稱UI線程,默認情況下,所有的操作都是在主線程上執行。如果需要執行比較耗時的任務(如下載文件、查詢數據庫),可創建其他線程來處理。
如果應用的業務邏輯比較復雜,可能需要創建多個線程來執行多個任務。這種情況下,代碼復雜難以維護,任務與線程的交互也會更加繁雜。要解決此問題,開發者可以使用TaskDispatcher來分發不同的任務。
TaskDispatcher介紹
TaskDispatcher是一個任務分發器,它是Ability分發任務的基本接口,隱藏任務所在線程的實現細節。
為保證應用有更好的響應性,我們需要設計任務的優先級。在UI線程上運行的任務默認以高優先級運行,如果某個任務無需等待結果,則可以用低優先級。
線程優先級介紹:
HIGH:最高任務優先級,比默認優先級、低優先級的任務有更高的幾率得到執行。
DEFAULT:默認任務優先級, 比低優先級的任務有更高的幾率得到執行。
LOW:低任務優先級,比高優先級、默認優先級的任務有更低的幾率得到執行。
TaskDispatcher具有多種實現,每種實現對應不同的任務分發器。在分發任務時可以指定任務的優先級,由同一個任務分發器分發出的任務具有相同的優先級。系統提供的任務分發器有GlobalTaskDispatcher、ParallelTaskDispatcher、SerialTaskDispatcher 、SpecTaskDispatcher。
實踐
1.同步派發任務syncDispatch
發任務并在當前線程等待任務執行完成。在返回前,當前線程會被阻塞
- /**
- * 同步派發任務
- */
- private void syncDispatch() {
- TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT);
- globalTaskDispatcher.syncDispatch(new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "sync task1 run");
- }
- });
- HiLog.info(LABEL_LOG, "after sync task1");
- globalTaskDispatcher.syncDispatch(new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "sync task2 run");
- }
- });
- HiLog.info(LABEL_LOG, "after sync task2");
- globalTaskDispatcher.syncDispatch(new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "sync task3 run");
- }
- });
- HiLog.info(LABEL_LOG, "after sync task3");
- }
運行之后查看日志:

從運行結果我們可以看到,只有在當前線程等待任務執行完成之后才會繼續往下執行,否則當前線程會被阻塞,所以在使用syncDispatch的時候我們需要注意,如果對syncDispatch使用不當, 將會導致死鎖。如下情形可能導致死鎖發生:
- 在專有線程上,利用該專有任務分發器進行syncDispatch。
- 在被某個串行任務分發器(dispatcher_a)派發的任務中,再次利用同一個串行任務分發器(dispatcher_a)對象派發任務。
- 在被某個串行任務分發器(dispatcher_a)派發的任務中,經過數次派發任務,最終又利用該(dispatcher_a)串行任務分發器派發任務。例如:dispatcher_a派發的任務使用dispatcher_b進行任務的派發,在dispatcher_b派發的任務中又利用dispatcher_a進行派發任務。
- 串行任務分發器(dispatcher_a)派發的任務中利用串行任務分發器(dispatcher_b)進行同步派發任務,同時dispatcher_b派發的任務中利用串行任務分發器(dispatcher_a)進行同步派發任務。在特定的線程執行順序下將導致死鎖。
2.異步派發任務asyncDispatch
派發任務,并立即返回,返回值是一個可用于取消任務的接口。
- /**
- * 異步派發任務
- */
- private void asyncDispatch() {
- TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT);
- Revocable revocable = globalTaskDispatcher.asyncDispatch(new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "async task1 run");
- }
- });
- HiLog.info(LABEL_LOG, "after async task1");
- }
運行之后查看日志:

從運行結果我們可以看到,只有在當前線程等待任務執行完成之后才會繼續往下執行,否則當前線程會被阻塞,所以在使用
3. 異步延遲派發任務delayDispatch
異步執行,函數立即返回,內部會在延時指定時間后將任務派發到相應隊列中。延時時間參數僅代表在這段時間以后任務分發器會將任務加入到隊列中,任務的實際執行時間可能晚于這個時間。具體比這個數值晚多久,取決于隊列及內部線程池的繁忙情況。
- /**
- * 異步延遲派發任務
- */
- private void delayDispatch() {
- final long callTime = System.currentTimeMillis();
- final long delayTime = 50L;
- TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT);
- Revocable revocable = globalTaskDispatcher.delayDispatch(new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "delayDispatch task1 run");
- final long actualDelay = System.currentTimeMillis() - callTime;
- HiLog.info(LABEL_LOG, "actualDelayTime >= delayTime: %{public}b", (actualDelay >= delayTime));
- }
- }, delayTime);
- HiLog.info(LABEL_LOG, "after delayDispatch task1");
- }
運行之后查看日志:

從運行結果我們可以看出,程序首先執行"after delayDispatch task1",然后執行"delayDispatch task1 run",最后執行"actualDelayTime >= delayTime: %{public}b", (actualDelay >= delayTime),這里 actualDelayTime >= delayTime: true可以看出延時時間參數僅代表在這段時間以后任務分發器會將任務加入到隊列中,任務的實際執行時間可能晚于這個時間。
4. 任務組Group
表示一組任務,且該組任務之間有一定的聯系,由TaskDispatcher執行createDispatchGroup創建并返回。將任務加入任務組,返回一個用于取消任務的接口。
- /**
- * 任務組
- */
- private void dispatchGroup() {
- String dispatcherName = "parallelTaskDispatcher";
- TaskDispatcher dispatcher = createParallelTaskDispatcher(dispatcherName, TaskPriority.DEFAULT);
- // 創建任務組。
- Group group = dispatcher.createDispatchGroup();
- // 將任務1加入任務組,返回一個用于取消任務的接口。
- dispatcher.asyncGroupDispatch(group, new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "download task1 is running");
- }
- });
- // 將與任務1相關聯的任務2加入任務組。
- dispatcher.asyncGroupDispatch(group, new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "download task2 is running");
- }
- });
- // 在任務組中的所有任務執行完成后執行指定任務。
- dispatcher.groupDispatchNotify(group, new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "the close task is running after all tasks in the group are completed");
- }
- });
- }
運行之后查看日志:

5. 同步設置屏障任務syncDispatchBarrier
在任務組上設立任務執行屏障,同步等待任務組中的所有任務執行完成,再執行指定任務。
- /**
- * 同步設置屏障任務
- */
- private void syncDispatchBarrier() {
- String dispatcherName = "parallelTaskDispatcher";
- TaskDispatcher dispatcher = createParallelTaskDispatcher(dispatcherName, TaskPriority.DEFAULT);
- // 創建任務組。
- Group group = dispatcher.createDispatchGroup();
- // 將任務加入任務組,返回一個用于取消任務的接口。
- dispatcher.asyncGroupDispatch(group, new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "task1 is running"); // 1
- }
- });
- dispatcher.asyncGroupDispatch(group, new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "task2 is running"); // 2
- }
- });
- dispatcher.syncDispatchBarrier(new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "barrier"); // 3
- }
- });
- HiLog.info(LABEL_LOG, "after syncDispatchBarrier"); // 4
- }
運行之后查看日志:

6. 異步設置屏障任務asyncDispatchBarrier
在任務組上設立任務執行屏障后直接返回,指定任務將在任務組中的所有任務執行完成后再執行。
- /**
- * 異步設置屏障任務
- */
- private void asyncDispatchBarrier() {
- TaskDispatcher dispatcher = createParallelTaskDispatcher("dispatcherName", TaskPriority.DEFAULT);
- // 創建任務組。
- Group group = dispatcher.createDispatchGroup();
- // 將任務加入任務組,返回一個用于取消任務的接口。
- dispatcher.asyncGroupDispatch(group, new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "task1 is running"); // 1
- }
- });
- dispatcher.asyncGroupDispatch(group, new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "task2 is running"); // 2
- }
- });
- dispatcher.asyncDispatchBarrier(new Runnable() {
- @Override
- public void run() {
- HiLog.info(LABEL_LOG, "barrier"); // 3
- }
- });
- HiLog.info(LABEL_LOG, "after asyncDispatchBarrier"); // 4
- }
運行之后查看日志:

總結
線程它就像一面雙刃劍,用的好的時候可以給我們帶來事半功倍等效果,用的不好時就會給我們帶來困擾,并且這個困擾還不是一時半會能解決掉的(因為發現問題的時候,往往是到了需要優化期了,各項業務相互牽扯),故在項目初期就需要嚴格考慮考量這些問題了。