鸿蒙的线程管理,看完浑身通透!

Answer 2021-09-16 13:47:50 4300

不同应用在各自独立的进程中运行。当应用以任何形式启动时,系统为其创建进程,该进程将持续运行。当进程完成当前任务处于等待状态,且系统资源不足时,系统自动回收。

在启动应用时,系统会为该应用创建一个称为“主线程”的执行线程。该线程随着应用创建或消失,是应用的核心线程。UI 界面的显示和更新等操作,都是在主线程上进行。

主线程又称 UI 线程,默认情况下,所有的操作都是在主线程上执行。如果需要执行比较耗时的任务(如下载文件、查询数据库),可创建其他线程来处理。

如果应用的业务逻辑比较复杂,可能需要创建多个线程来执行多个任务。这种情况下,代码复杂难以维护,任务与线程的交互也会更加繁杂。

要解决此问题,开发者可以使用 TaskDispatcher 来分发不同的任务。

TaskDispatcher 介绍

TaskDispatcher 是一个任务分发器,它是 Ability 分发任务的基本接口,隐藏任务所在线程的实现细节。

为保证应用有更好的响应性,我们需要设计任务的优先级。在 UI 线程上运行的任务默认以高优先级运行,如果某个任务无需等待结果,则可以用低优先级。

线程优先级介绍:

HIGH:最高任务优先级,比默认优先级、低优先级的任务有更高的几率得到执行。

DEFAULT:默认任务优先级, 比低优先级的任务有更高的几率得到执行。

LOW:低任务优先级,比高优先级、默认优先级的任务有更低的几率得到执行。

TaskDispatcher 具有多种实现,每种实现对应不同的任务分发器。在分发任务时可以指定任务的优先级,由同一个任务分发器分发出的任务具有相同的优先级。

系统提供的任务分发器有:

GlobalTaskDispatcher

ParallelTaskDispatcher

SerialTaskDispatcher

SpecTaskDispatcher

实践

①同步派发任务 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)进行同步派发任务。在特定的线程执行顺序下将导致锁。

②异步派发任务 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");
}

运行之后查看日志:

图片

从运行结果我们可以看到,只有在当前线程等待任务执行完成之后才会继续往下执行,否则当前线程会被阻塞,所以在使用。

③异步延迟派发任务 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 可以看出延时时间参数仅代表在这段时间以后任务分发器会将任务加入到队列中,任务的实际执行时间可能晚于这个时间。

④任务组 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");
        }
    });
}

运行之后查看日志:

⑤同步设置屏障任务 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
}

运行之后查看日志:

⑥异步设置屏障任务 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

    }

运行之后查看日志:

总结

线程它就像一面双刃剑,用的好的时候可以给我们带来事半功倍等效果,用的不好时就会给我们带来困扰。

并且这个困扰还不是一时半会能解决掉的(因为发现问题的时候,往往是到了需要优化期了,各项业务相互牵扯),故在项目初期就需要严格考虑考量这些问题了。

来源:鸿蒙技术社区

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
Answer
红包 93 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
Answer
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区