观察者模式中多线程执行订阅事件并顺序执行的问题
对事件发布订阅模式中启动线程执行操作,但又要保证线程顺序执行的一些思考和实践,在开发过程中,经常会遇到需要使用事件来触发方法执行的情况,比如CS中按钮的点击事件,鼠标移动事件,键盘监听事件等等,有时候需要执行比较耗时的任务,但并不希望阻塞主线程,导致界面卡顿,使用线程有不能保证线程像同步的执行顺序一样顺序执行,因为有时候事件是系统触发的所以没有办法等待,所以在这儿记录下这个解决思路。
模拟情景:模拟事件订阅的方法被多线程执行。
首先声明一个TaskEventQueue类并使用单例模式,公开Regist方法,默认通过启用线程的方式执行订阅的方法,公开Invoke方法来发布事件。
通过下面的方式订阅事件,现在遇到的问题是订阅的方法不能按顺序执行 ,
所以我考虑使用延续任务,来解决这一问题。
修改构造函数并添加一个Task属性,延续任务必须是上一个任务处于正常执行完毕退出的状态才能生效。
修改Regist方法通过重复指向的方式不断叠加延续任务。
此时输出的结果为:
普通的主线程执行结果为:
疑惑:是否有必要这么做?
有时候会想,为什么不直接在发布事件时开一个线程执行呢?修改代码,试一下是可以的
答案是可以的,结果确实会顺序执行,结果为:
但这是我们自己的代码可以随意更改,如果是系统发布的事件,就不是我们能干涉的了,不能要求系统也这么做。
上述的方式其实只适用于业务场景并发量小的情况,在bs服务端暂时还没有这样的需求,但CS端可能会遇到。
注意:当前模拟场景中事件的发布假设是无法更改的。
代码部分:
public class TaskEventQueue
{
TaskEventQueue()
{
ComplateTask = new Task(() => { });
ComplateTask.Start();
}volatile Task ComplateTask;
public event Action SampleEvent;
volatile static TaskEventQueue taskEventQueue;
public static TaskEventQueue GetSingletionInstance()=>taskEventQueue??(taskEventQueue = new TaskEventQueue());public void Regist(Action action)
{
//SampleEvent += action;//主线程同步执行方式
SampleEvent += () =>//采用多线程方式
{
if (action == null)
{
throw new ArgumentException();
}
ComplateTask = ComplateTask.ContinueWith(t => action());
};
}public void Invok()
{
SampleEvent.Invoke();
}
}