1、概述
如果应用的业务逻辑比较复杂,可能需要创建多个线程来执行多个任务。这种情况下,代码复杂难以维护,任务与线程的交互也会更加繁杂。要解决此问题,开发者可以使用“
TaskDispatcher
”来分发不同的任务。
在启动应用时,系统会为该应用创建一个称为“主线程”的执行线程。该线程随着应用创建或消失,是应用的核心线程。
UI
界面的显示和更新等操作,都是在主线程上进行。主线程又称UI
线程,默认情况下,所有的操作都是在主线程上执行。
- 多线程可用在需要处理长时间等待的任务中,例如网络访问和数据库访问等。
- 在
Harmonyos
中,TaskDispatcher
任务分发器可用来分发不同的任务。 - 任务的优先级分为
HIGH
、DEFAULT
和LOW
。
优先级 | 详细描述 |
---|---|
HIGH | 最高任务优先级,比默认优先级、低优先级的任务有更高的几率得到执行。 |
DEFAULT | 默认任务优先级, 比低优先级的任务有更高的几率得到执行。 |
LOW | 低任务优先级,比高优先级、默认优先级的任务有更低的几率得到执行。 |
2、任务分发器
TaskDispatcher
是一个任务分发器,它是Ability
分发任务的基本接口,隐藏任务所在线程的实现细节
2.1、TastDispatcher接口类的四种实现
TaskDispatcher
具有多种实现,每种实现对应不同的任务分发器。在分发任务时可以指定任务的优先级,由同一个任务分发器分发出的任务具有相同的优先级。系统提供的任务分发器有GlobalTaskDispatcher
、ParallelTaskDispatcher
、SerialTaskDispatcher
、SpecTaskDispatcher
。
2.1.1、GlobalTaskDispatcher全局并发任务分发器
全局并发任务分发器,由
Ability
执行getGlobalTaskDispatcher()
获取。适用于任务之间没有联系的情况。一个应用只有一个GlobalTaskDispatcher
,它在程序结束时才被销毁。
2.1.2、ParallelTaskDispatcher并发任务分发器
并发任务分发器,由
Ability
执行createParallelTaskDispatcher()
创建并返回。与GlobalTaskDispatcher
不同的是,ParallelTaskDispatcher
不具有全局唯一性,可以创建多个。开发者在创建或销毁dispatcher
时,需要持有对应的对象引用 。
2.1.3、SerialTaskDispatcher串行任务分发器
串行任务分发器,由
Ability
执行createSerialTaskDispatcher()
创建并返回。由该分发器分发的所有的任务都是按顺序执行,但是执行这些任务的线程并不是固定的。如果要执行并行任务,应使用ParallelTaskDispatcher
或者GlobalTaskDispatcher
,而不是创建多个SerialTaskDispatcher
。如果任务之间没有依赖,应使用GlobalTaskDispatcher
来实现。它的创建和销毁由开发者自己管理,开发者在使用期间需要持有该对象引用。
2.1.4、SpecTaskDispatcher专有任务分发器
专有任务分发器,绑定到专有线程上的任务分发器。目前已有的专有线程是主线程。
UITaskDispatcher
和MainTaskDispatcher
都属于SpecTaskDispatcher
。建议使用UITaskDispatcher
。
UITaskDispatcher
:绑定到应用主线程的专有任务分发器, 由Ability
执行getUITaskDispatcher()
创建并返回。 由该分发器分发的所有的任务都是在主线程上按顺序执行,它在应用程序结束时被销毁
2.2、四种任务分发器的获取
//1、获取全局并发任务分发器,一个应用程序只有一个
TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(null);
//2、获取并发任务分发器,可以创建多个,创建的时候,传入的第一个参数为一个字符串,表示一个并发任务分发器的引用,在销毁的时候需要指定对应的引用来销毁指定的分发器。
TaskDispatcher parallelTaskDispatcher = createParallelTaskDispatcher("parallelTaskDispatcher", null);
//3、获取串行任务分发器,类似有序列表。
TaskDispatcher serialTaskDispatcher = createSerialTaskDispatcher("serialTaskDispatcher",null);
//4、获取专有任务分发器
//4、1.获取主线程任务分发器
TaskDispatcher mainTaskDispatcher = getMainTaskDispatcher();
//4、2.获取UI线程任务分发器
TaskDispatcher uiTaskDispatcher = getUITaskDispatcher();
2.3、八个派发任务的API应用
2.3.1、syncDispatch同步派发任务
派发任务并在当前线程等待任务执行完成。在返回前,当前线程会被阻塞。
/**
* @description 同步派发任务,派发任务并在当前线程等待任务执行完成。在返回前,当前线程会被阻塞。
* @author PengHuAnZhi
* @date 2021/1/13 13:46
*/
private void syncDispatch() {
globalTaskDispatcher.syncDispatch(() -> HiLog.info(label, "同步任务1执行"));
HiLog.info(label, "同步任务1执行完毕");
globalTaskDispatcher.syncDispatch(() -> HiLog.info(label, "同步任务2执行"));
HiLog.info(label, "同步任务2执行完毕");
globalTaskDispatcher.syncDispatch(() -> HiLog.info(label, "同步任务3执行"));
HiLog.info(label, "同步任务3执行完毕");
}
同步任务严格按照任务分配顺序执行
如果对
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.3.2、asyncDispatch异步派发任务
派发任务,并立即返回,返回值是一个可用于取消任务的接口。
/**
* @description 异步派发任务,派发任务,并立即返回,返回值是一个可用于取消任务的接口。
* @author PengHuAnZhi
* @date 2021/1/13 13:47
*/
private void asyncDispatch() {
Revocable revocable1 = globalTaskDispatcher.asyncDispatch(() -> HiLog.info(label, "异步任务1执行"));
Revocable revocable2 = globalTaskDispatcher.asyncDispatch(() -> HiLog.info(label, "异步任务2执行"));
HiLog.info(label, "外部代码执行");
}
异步任务的执行顺序不固定,是随机的
由于返回值为一个
Revocable
,此接口可用于撤销任务分发,尝试一下
/**
* @description 异步派发任务,派发任务,并立即返回,返回值是一个可用于取消任务的接口。
* @author PengHuAnZhi
* @date 2021/1/13 13:47
*/
private void asyncDispatch() {
Revocable revocable1 = globalTaskDispatcher.asyncDispatch(() -> HiLog.info(label, "异步任务1执行"));
Revocable revocable2 = globalTaskDispatcher.asyncDispatch(() -> HiLog.info(label, "异步任务2执行"));
revocable2.revoke();
HiLog.info(label, "外部代码执行");
}
2.3.3、delayDispatch异步延迟派发任务
异步执行,函数立即返回,内部会在延时指定时间后将任务派发到相应队列中。延时时间参数仅代表在这段时间以后任务分发器会将任务加入到队列中,任务的实际执行时间可能晚于这个时间。具体比这个数值晚多久,取决于队列及内部线程池的繁忙情况。
/**
* @description 异步延迟派发任务:异步执行,函数立即返回,内部会在延时指定时间后将任务派发到相应队列中。
* 延时时间参数仅代表在这段时间以后任务分发器会将任务加入到队列中,任务的实际执行时间可能
* 晚于这个时间。具体比这个数值晚多久,取决于队列及内部线程池的繁忙情况。
* @author PengHuAnZhi
* @date 2021/1/13 13:48
*/
private void delayDispatch() {
//当前时间
final long callTime = System.currentTimeMillis();
//延迟时间
final long delayTime = 50;
Revocable revocable = globalTaskDispatcher.delayDispatch(() -> {
HiLog.info(label, "异步延迟任务1开始执行");
//获取当前任务执行时间和开始派发任务的时间差
final long actualDelayMs = System.currentTimeMillis() - callTime;
HiLog.info(label, "任务实际延迟执行时间比理论延迟执行时间晚:" + (actualDelayMs - delayTime) + "ms");
}, delayTime);
HiLog.info(label, "外部代码执行");
}
2.3.4、group任务组
表示一组任务,且该组任务之间有一定的联系,由
TaskDispatcher
执行createDispatchGroup
创建并返回。将任务加入任务组,返回一个用于取消任务的接口。
/**
* @description 任务组:表示一组任务,且该组任务之间有一定的联系,由 TaskDispatcher 执行 createDispatchGroup
* 创建并返回。将任务加入任务组,返回一个用于取消任务的接口。
* @author PengHuAnZhi
* @date 2021/1/13 13:48
*/
private void group() {
//创建任务组
Group group = parallelTaskDispatcher.createDispatchGroup();
// 将任务 1 加入任务组,
parallelTaskDispatcher.asyncGroupDispatch(group, () -> HiLog.info(label, "任务1加入任务组"));
// 将任务 2 加入任务组,
parallelTaskDispatcher.asyncGroupDispatch(group, () -> HiLog.info(label, "任务2加入任务组"));
// 在任务组中的所有任务执行完成后执行指定任务。
parallelTaskDispatcher.groupDispatchNotify(group, () -> HiLog.info(label, "任务组执行完毕"));
}
可以看到异步任务组中的任务顺序不是固定的,而任务组后置任务是肯定在任务组所有任务都执行完毕再执行的
2.3.5、revocable取消任务
Revocable
是取消一个异步任务的接口。异步任务包括通过asyncDispatch
、delayDispatch
、asyncGroupDispatch
派发的任务。如果任务已经在执行中或执行完成,则会返回取消失败。
/**
* @description 取消任务:Revocable 是取消一个异步任务的接口。异步任务包括通过 asyncDispatch、delayDispatch、asyncGroupDispatch 派发的任务。如果任务已经在执行中或执行完成,则会返回取消失败。
* @author PengHuAnZhi
* @date 2021/1/13 13:48
*/
private void revocable() {
Revocable revocable1 = globalTaskDispatcher.asyncDispatch(() -> HiLog.info(label, "任务1执行了"));
Revocable revocable2 = globalTaskDispatcher.delayDispatch(() -> HiLog.info(label, "任务2执行了"), 10);
boolean revoke1 = revocable1.revoke();
boolean revoke2= revocable2.revoke();
HiLog.info(label, "任务1是否撤销:"+ revoke1);
HiLog.info(label, "任务2是否撤销:"+ revoke2);
}
2.3.6、syncDispatchBarrier同步设置屏障任务
在任务组上设立任务执行屏障,同步等待任务组中的所有任务执行完成,再执行指定任务。在全局并发任务分发器(
GlobalTaskDispatcher
)上同步设置任务屏障,将不会起到屏障作用。
/**
* @description 同步设置屏障任务:在任务组上设立任务执行屏障,同步等待任务组中的所有任务执行完成,再执行指定任务。
* @author PengHuAnZhi
* @date 2021/1/13 13:49
*/
private void syncDispatchBarrier( ) {
// 创建任务组。
Group group = parallelTaskDispatcher.createDispatchGroup();
// 将任务加入任务组,返回一个用于取消任务的接口。
parallelTaskDispatcher.asyncGroupDispatch(group, () -> {
HiLog.info(label, "异步任务1执行");
});
parallelTaskDispatcher.asyncGroupDispatch(group, () -> {
HiLog.info(label, "异步任务2执行");
});
parallelTaskDispatcher.syncDispatchBarrier(() -> {
HiLog.info(label, "任务组执行完毕,执行后置同步任务");
});
HiLog.info(label, "任务组执行完毕,后置同步任务执行完毕");
}
同步设置屏障任务,任务组中无论是同步还是异步,后置同步任务都是在任务组执行完毕后再执行
2.3.7、asyncDispatchBarrier异步设置屏障任务
在任务组上设立任务执行屏障后直接返回,指定任务将在任务组中的所有任务执行完成后再执行。在全局并发任务分发器(
GlobalTaskDispatcher
)上异步设置任务屏障,将不会起到屏障作用。可以使用并发任务分发器(ParallelTaskDispatcher
)分离不同的任务组,达到微观并行、宏观串行的行为。
/**
* @description 异步设置屏障任务:在任务组上设立任务执行屏障后直接返回,指定任务将在任务组中的所有任务执行完成后再执行。
* @author PengHuAnZhi
* @date 2021/1/13 13:49
*/
private void asyncDispatchBarrier() {
// 创建任务组。
Group group = parallelTaskDispatcher.createDispatchGroup();
// 将任务加入任务组,返回一个用于取消任务的接口。
parallelTaskDispatcher.asyncGroupDispatch(group, () -> {
HiLog.info(label, "异步任务1执行");
});
parallelTaskDispatcher.asyncGroupDispatch(group, () -> {
HiLog.info(label, "异步任务2执行");
});
parallelTaskDispatcher.asyncDispatchBarrier(() -> {
HiLog.info(label, "后置异步任务执行");
});
HiLog.info(label, "外部任务执行");
}
异步设置屏障任务,后置异步任务始终在任务组所有任务执行完毕后再执行,其他任何任务的执行顺序对他来说都不关心,如果它后面还有其他任务,是有可能在它前面执行的
2.3.8、applyDispatch执行多次任务
对指定任务执行多次。
/**
* @description 执行多次任务:对指定任务执行多次。
* @author PengHuAnZhi
* @date 2021/1/13 13:50
*/
private void applyDispatch() {
//总执行次数
final int total = 10;
//倒数计时器
final CountDownLatch latch = new CountDownLatch(total);
//长整形列表数组
final ArrayList<Long> indexList = new ArrayList<>(total);
//执行多次任务
parallelTaskDispatcher.applyDispatch((index) -> {
indexList.add(index);
latch.countDown();
HiLog.info(label, "" + index);
}, total);
//设置任务超时
try {
//使当前线程等待,直到倒计时计数到0为止。
latch.await();
} catch (InterruptedException e) {
HiLog.info(label, "任务超时!");
}
HiLog.info(label, "执行了" + indexList.size() + "次");
}
3、线程间通信
3.1、概念
在开发过程中,开发者经常需要在当前线程中处理下载任务等较为耗时的操作,但是又不希望当前的线程受到阻塞。此时,就可以使用
EventHandler
机制。EventHandler
是HarmonyOS
用于处理线程间通信的一种机制,可以通过EventRunner
创建新线程,将耗时的操作放到新线程上执行。这样既不阻塞原来的线程,任务又可以得到合理的处理。比如:主线程使用EventHandler
创建子线程,子线程做耗时的下载图片操作,下载完成后,子线程通过EventHandler
通知主线程,主线程再更新UI
。(看完描述我觉得这和Android
里面的Handler
类似啊)
3.2、三种线程通信方式
3.2.1、投递 InnerEvent 事件
myEventHandler1 = new EventHandler(runnerA) {
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
if (event == null) {
return;
}
int eventId = event.eventId;
switch (eventId) {
case 1: {
HiLog.info(hiLogLabel, "1");
break;
}
case 2: {
HiLog.info(hiLogLabel, "2");
break;
}
case 3: {
HiLog.info(hiLogLabel, "3");
break;
}
default:
break;
}
}
};
/**
* EventHandler 投递 InnerEvent 事件
*/
private void initInnerEvent() {
// 向线程A发送事件
long param = 0;
Object object = EventRunner.current();
InnerEvent event = InnerEvent.get(1, param, object);
myEventHandler1.sendEvent(event);
runnerA.run();
runnerA.stop();
}
3.2.2、投递 Runnable 任务
myEventHandler2 = new EventHandler(runnerB) {
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
}
};
/**
* EventHandler 投递 Runnable 任务
*/
private void initRunnable() {
Runnable task1 = () -> HiLog.info(hiLogLabel, "Runnable1执行");
Runnable task2 = () -> HiLog.info(hiLogLabel, "Runnable2执行");
myEventHandler2.postTask(task2, 0, EventHandler.Priority.IMMEDIATE);
myEventHandler2.postTask(task1, 0, EventHandler.Priority.IMMEDIATE);
runnerB.run();
runnerB.stop();
}
3.2.3、在新创建的线程里投递事件到原线程
myEventHandler3 = new EventHandler(runnerC) {
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
if (event == null) {
return;
}
int eventId = event.eventId;
Object object = event.object;
switch (eventId) {
case 1:
// 待执行的操作,由开发者定义
break;
case 2:
// 将原先线程的EventRunner实例投递给新创建的线程
if (object instanceof EventRunner) {
EventRunner runner2 = (EventRunner) object;
}
// 将原先线程的EventRunner实例与新创建的线程的EventHandler绑定
EventHandler myHandler = new EventHandler(runnerC) {
@Override
public void processEvent(InnerEvent event) {
//需要在原先线程执行的操作
HiLog.info(hiLogLabel,"原线程操作");
}
};
InnerEvent event2 = InnerEvent.get(1, 0, null);
myHandler.sendEvent(event2); // 投递事件到原先的线程
break;
default:
break;
}
}
};
/**
* 在新创建的线程里投递事件到原线程
*/
private void initNewToOld() {
int eventId = 2;
long param = 0;
Object object = EventRunner.current();
InnerEvent event = InnerEvent.get(eventId, param, object);
myEventHandler3.sendEvent(event);
runnerC.run();
runnerC.stop();
}
3.2.4、完整代码
/**
* @description 糊里糊涂的线程通信
* @author PengHuAnZhi
* @date 2021/1/13 20:46
*/
public class MainAbilitySlice extends AbilitySlice {
private static HiLogLabel hiLogLabel = new HiLogLabel(3, 1, "MainAbilitySlice");
private EventRunner runnerA;
private EventRunner runnerB;
private EventRunner runnerC;
private EventHandler myEventHandler1;
private EventHandler myEventHandler2;
private EventHandler myEventHandler3;
private Button mBtn1;
private Button mBtn2;
private Button mBtn3;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
mBtn1 = (Button) findComponentById(ResourceTable.Id_btn1);
mBtn2 = (Button) findComponentById(ResourceTable.Id_btn2);
mBtn3 = (Button) findComponentById(ResourceTable.Id_btn3);
mBtn1.setClickedListener(component -> initInnerEvent());
mBtn2.setClickedListener(component -> initRunnable());
mBtn3.setClickedListener(component -> initNewToOld());
runnerA = EventRunner.create(false);
runnerB = EventRunner.create(false);
runnerC = EventRunner.create(false);
myEventHandler1 = new EventHandler(runnerA) {
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
if (event == null) {
return;
}
int eventId = event.eventId;
switch (eventId) {
case 1: {
HiLog.info(hiLogLabel, "1");
break;
}
case 2: {
HiLog.info(hiLogLabel, "2");
break;
}
case 3: {
HiLog.info(hiLogLabel, "3");
break;
}
default:
break;
}
}
};
myEventHandler2 = new EventHandler(runnerB) {
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
}
};
myEventHandler3 = new EventHandler(runnerC) {
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
if (event == null) {
return;
}
int eventId = event.eventId;
Object object = event.object;
switch (eventId) {
case 1:
// 待执行的操作,由开发者定义
break;
case 2:
// 将原先线程的EventRunner实例投递给新创建的线程
if (object instanceof EventRunner) {
EventRunner runner2 = (EventRunner) object;
}
// 将原先线程的EventRunner实例与新创建的线程的EventHandler绑定
EventHandler myHandler = new EventHandler(runnerC) {
@Override
public void processEvent(InnerEvent event) {
//需要在原先线程执行的操作
HiLog.info(hiLogLabel,"原线程操作");
}
};
InnerEvent event2 = InnerEvent.get(1, 0, null);
myHandler.sendEvent(event2); // 投递事件到原先的线程
break;
default:
break;
}
}
};
}
/**
* EventHandler 投递 InnerEvent 事件
*/
private void initInnerEvent() {
// 向线程A发送事件
long param = 0;
Object object = EventRunner.current();
InnerEvent event = InnerEvent.get(1, param, object);
myEventHandler1.sendEvent(event);
runnerA.run();
runnerA.stop();
}
/**
* EventHandler 投递 Runnable 任务
*/
private void initRunnable() {
Runnable task1 = () -> HiLog.info(hiLogLabel, "Runnable1执行");
Runnable task2 = () -> HiLog.info(hiLogLabel, "Runnable2执行");
myEventHandler2.postTask(task2, 0, EventHandler.Priority.IMMEDIATE);
myEventHandler2.postTask(task1, 0, EventHandler.Priority.IMMEDIATE);
runnerB.run();
runnerB.stop();
}
/**
* 在新创建的线程里投递事件到原线程
*/
private void initNewToOld() {
int eventId = 2;
long param = 0;
Object object = EventRunner.current();
InnerEvent event = InnerEvent.get(eventId, param, object);
myEventHandler3.sendEvent(event);
runnerC.run();
runnerC.stop();
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}