[英]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.