2013-11-26 3 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().

События могут также быть буферизованы в любое время, если 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."); 
     } 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^