觀察者模式中多執行緒執行訂閱事件並順序執行的問題
對事件釋出訂閱模式中啟動執行緒執行操作,但又要保證執行緒順序執行的一些思考和實踐,在開發過程中,經常會遇到需要使用事件來觸發方法執行的情況,比如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();
}
}