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