2014-11-24 2 views
5

У меня есть служба, которая обеспечивает одновременное отображение всего одного всплывающего окна. AddPopupAsync можно назвать одновременно, т.е. всплывающее окно открыто, а еще 10 AddPopupAsync запросов заглядывать Код:.Как сделать асинхронные методы, которые используют поточный поток в очереди

public async Task<int> AddPopupAsync(Message message) 
{ 
    //[...] 

    while (popupQueue.Count != 0) 
    { 
     await popupQueue.Peek(); 
    } 

    return await Popup(interaction); 
} 

Но я могу обнаружить два ненужных вещей, которые могут произойти из-за отсутствия безопасности потока:

  1. Если очередь пуста, Peek сгенерирует исключение
  2. Если нить А выгружены перед первым оператором в Всплывающие, другой поток B не будет ждать в ожидании всплывающего А так как очередь пуста.

метод Popup работает с TaskCompletionSource и перед вызовом метода SetResult, popupQueue.Dequeue() называется.

Я думаю об использовании атомной TryPeek от ConcurrentQueue для того, чтобы сделать # 1 потокобезопасным:

do 
{ 
    Task<int> result; 

    bool success = popupQueue.TryPeek(out result); 

    if (!success) break; 
    await result; 
} 
while (true); 

Затем я прочитал о AsyncProducerConsumerCollection, но я не уверен, если это самое простое решение.

Как я могу обеспечить безопасность потока простым способом? Спасибо.

+1

Я думаю, вы ответили на свой вопрос. 'ConcurrentQueue ' имеет то, что вам нужно встроенный. –

+0

Ваша текущая семантика состоит в том, что все вызовы 'AddPopupAsync' не будут завершены, пока не будут показаны все всплывающие окна (а не только тот, который показывает этот конкретный вызов). Вы уверены, что это семантика, которую вы хотите? –

+0

@ Stephen Cleary Поскольку PopupView и PopupViewModel являются одиночными, состояние модального всплывающего окна изменяется всякий раз, когда вызывается Popup. И именно поэтому я использую очередь. – user4287276

ответ

8

Чтобы добавить поточную безопасность, вы должны использовать ConcurrentQueue. Это поточно-безопасная реализация очереди, и вы можете использовать ее так же, как если бы вы использовали очередь, не беспокоясь о параллелизме.

Однако, если вы хотите очередь вы можете await асинхронно (что означает, что вы не заняты-ожидание или блокирует поток во время ожидания) вы должны использовать TPL Dataflow-х BufferBlock, который очень похож на AsyncProducerConsumerCollection только уже реализован для вас МС:

var buffer = new BufferBlock<int>(); 
await buffer.SendAsync(3); // Instead of enqueue. 
int item = await buffer.ReceiveAsync(); // instead of dequeue. 

ConcurrentQueue прекрасно подходит для нарезания безопасности, но расточителен в высоких уровнях конкуренции. BufferBlock не только поточно-безопасный, но и дает асинхронную координацию (обычно между потребителем и производителем).

+0

Но ConcurrentQueue не указывает, что код с использованием ConcurrentQueue является потокобезопасным, не так ли (см. №2 в моем вопросе)? – user4287276

+0

@ user4287276 'ConcurrentQueue' гарантирует, что каждый вызов одного из его методов сам по себе является потокобезопасным. Это не механизм блокировки. Если вам нужна синхронизация в коде, вы должны использовать 'lock' (или' AsyncLock', если это необходимо). – i3arnon