观察者模式中多线程执行订阅事件并顺序执行的问题

       对事件发布订阅模式中启动线程执行操作,但又要保证线程顺序执行的一些思考和实践,在开发过程中,经常会遇到需要使用事件来触发方法执行的情况,比如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();
        }
    }