2013-11-26 38 views
0

我有一个应用程序从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); 
     } 
    } 
} 
+0

看那'ConcurrentQueue '类,这可能是在这种情况下,事件流之间的调停人。或者,[Rx](http://msdn.microsoft.com/en-us/data/gg577609.aspx)也许能够帮助您,因为它可以将事件视为来源。 –

+0

这就是我现在使用的实现。我有一个很好的设计模式,或者我应该想到完全不同的东西吗? –

+0

对我来说看起来不错。 (不喜欢一些命名约定,但这不是问题的关键......) –

回答

0

我最初的解决方案是不令人满意的:after ProcessPendingEvents()完成,但被释放锁之前,其他事件可能是直到下一次拨打电话ProcessPendingEvents()

活动也可以在任何时候bufferred如果API发回不同的线程两个事件,我是缺乏关键的方式尽快锁定被释放消费这些事件。

我最终实现更清洁的生产者/消费者模式时API事件要处理,使用BlockingCollection控制。下面是那些有兴趣谁的相应代码:

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."); 
     } 
    } 
}