簡體   English   中英

帶鎖事件處理的最有效設計模式

[英]Most efficient design pattern for event handling with locking

我有一個應用程序,該應用程序從API異步接收事件,並且可以在此API上同步調用方法。

為了線程安全,我需要鎖定應用程序中的每個同步函數和每個事件處理程序。

但是,同步調用API方法可能會導致API在其他線程上引發事件,並在返回之前等待事件被處理。

因此,這可能會導致死鎖,因為API將等待事件的繼續處理,但是在我的類中,同步對象將被兩個不同的線程擊中,並且程序將掛起。

我當前的想法是嘗試鎖定而不是鎖定事件處理程序,並且在不可能的情況下(例如,如果事件是由另一個線程上的同步調用導致的)將事件緩沖在隊列/消息泵中。

在釋放對同步函數調用的鎖定之前,然后我將調用ProcessPendingEvents()函數,以便可以處理事件而不會出現死鎖。

對於這種情況,您有沒有建議的設計模式? 我對任何事情都開放。

這是一個簡單的示例來說明我當前的暫定實現。 我的真正目的是使類盡可能地以單線程方式運行:

class APIAdapter {
    readonly object AdapterLock = new object();
    private readonly ConcurrentQueue<Tuple<object, EventArgs>> PendingEvents = new ConcurrentQueue<Tuple<object, EventArgs>>();
    ExternalAPI API = new ExternalAPI();

    APIAdapter() {
        ExternalAPI.Data += ExternalAPI_Data;
    }

    public void RequestData() {
        lock (this.AdapterLock) {
            this.ExternalAPI.SynchronousDataRequest(); //Will cause the API to raise the Data event would therefore deadlock if I had a simple lock() in ExternalAPI_Data.
            this.ProcessPendingEvents();
        }
    }

    private void ExternalAPI_Data(object sender, EventArgs e) {
        if (!Monitor.TryEnter(this.AdapterLock)) {
            this.PendingEvents.Enqueue(Tuple.Create(sender, e));
            return;
        }
        Console.Write("Received event.");
        Monitor.Exit(this.AdapterLock);
    }

    private void ProcessPendingEvents() {
        Tuple<object, EventArgs> ev;
        while (this.PendingEvents.TryDequeue(out ev)) {
            ExternalAPI_Data(ev.Item1, ev.Item2);
        }
    }
}

我最初的解決方案並不令人滿意: after ProcessPendingEvents()完成之后,但在釋放鎖之前,可以緩沖其他事件,直到下一次調用ProcessPendingEvents()時才引發其他事件。

如果API在不同的線程上發送回兩個事件,則事件也可以在任何時候進行緩沖,而我非常缺乏一種在釋放鎖后立即消耗這些事件的方法。

我最終使用BlockingCollection實現了更為簡潔的生產者/消費者模式,以控制何時處理API事件。 以下是有興趣者的相應代碼:

class APIAdapter {
    readonly object AdapterLock = new object();
    private readonly BlockingCollection<Tuple<object, EventArgs>> PendingEvents = new BlockingCollection<Tuple<object, EventArgs>>();
    ExternalAPI API = new ExternalAPI();

    APIAdapter() {
        ExternalAPI.Data += ExternalAPI_Data;
        Task.Factory.StartNew(Consume, TaskCreationOptions.LongRunning);
    }

    public void Consume() {
        foreach (var e in this.PendingEvents.GetConsumingEnumerable()) {
            if (this.PendingEvents.IsAddingCompleted) return;
            ProcessData(e.Item1, e.Item2);
        }
    }

    public void RequestData() {
        lock (this.AdapterLock) {
            this.ExternalAPI.SynchronousDataRequest(); 
        }
    }

    private void ExternalAPI_Data(object sender, EventArgs e) {
        this.PendingEvents.Add(Tuple.Create(sender, e));
    }

    private void ProcessData(object sender, EventArgs e) {
        lock (this.AdapterLock) {
            Console.Write("Received event.");
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM