2015-08-19 3 views
8

В принципе, я хочу сделать несколько асинхронных запросов на сервере tcp. В настоящее время у меня есть рабочий клиент, который только синхронно и блокирует пользовательский интерфейс при каждом сетевом вызове. Так как множество запросов может произойти почти в то же самое время, я пытался сделать это:Асинхронный замок не разрешен

private object readonly readLock = new object(); 
public async Task UpdateDetailsAsync() 
{ 
    //I want every request to wait their turn before requesting (using the connection) 
    //to prevent a read call from catching any data from another request 
    lock (readLock) 
    { 
     Details details = await connection.GetDetailsAsync(); 
     detailsListBox.Items = details; 
    } 
} 

Я уверен, что это не хорошо использовать замок, но это единственный способ, которым я могу думать о том, что могли бы сделать звонки ждать за свою очередь. Есть ли объект, который я могу использовать для достижения такого поведения? Я думал, что монитор будет таким же, поэтому я не пробовал (я понимаю, что это многопоточность, но это все, что мне знакомо ...)

+0

Можете ли вы поделиться тем, как вы определяете 'readLock'? Возможно, в неправильном контексте блокировки? – code4life

+0

Я обновил код, включив в него элемент readLock, но это не проблема. 'await' вообще не может использоваться в методе async, не будет компилироваться. –

+0

Одним из способов сделать это было бы превращение ваших вызовов в очередь. блокировка добавления действий в очередь, а не самих операций. завершите очередь в фоновом режиме. –

ответ

15

Похоже, проблема в том, что потоки будут блокироваться приобретая замок, поэтому ваш метод не полностью асинхронный. Для решения этой проблемы можно использовать SemaphoreSlim.WaitAsync

private readonly SemaphoreSlim readLock = new SemaphoreSlim(1, 1); 
public async Task UpdateDetailsAsync() 
{ 
    //I want every request to wait their turn before requesting (using the connection) 
    //to prevent a read call from catching any data from another request 
    try 
    { 
     await readLock.WaitAsync(); 
     Details details = await connection.GetDetailsAsync(); 
     detailsListBox.Items = details; 
    } 
    finally 
    { 
     readLock.Release(); 
    } 
} 
+0

Если я правильно понимаю семафоры, верный способ инстанцировать в моем случае - это «новый SemaphoreSlim (0, 1)», если мне нужен только один за раз? –

+0

Да, это так. Спасибо за исправление. Отредактировал мой ответ. –

+1

Незначительная коррекция снова, я реализовал все это, он работает отлично. единственное, значение iintial должно быть '1', а не' 0'. каждый раз, когда мы вызываем WaitAsync(), и он возвращается, счет уменьшается. Благодаря! –

6

Эта проблема была решена аккуратно пакетом Nito.AsyncEx NuGet, который имеет более чем 50 000 загрузок по состоянию на август 2015 года

От ReadMe:

AsyncEx A вспомогательная библиотека для async/await.

Поддерживает .NET 4.5/4.0, iOS, Android, Windows Store 8.0, Windows Phone Silverlight 8.0/7.5, приложения для Windows Phone 8.1, Silverlight 5.0/4.0 и все их портативные библиотеки.

[надрез]

AsyncLock

Многие разработчики начинают использовать эту библиотеку для AsyncLock, с асинхронным-совместимый механизм взаимного исключения. Использование AsyncLock прост:

private readonly AsyncLock _mutex = new AsyncLock(); 
public async Task UseLockAsync() 
{ 
    // AsyncLock can be locked asynchronously 
    using (await _mutex.LockAsync()) 
    { 
    // It's safe to await while the lock is held 
    await Task.Delay(TimeSpan.FromSeconds(1)); 
    } 
} 

Смотрите C# source code on GitHub или установить пакет NuGet Nito.AsyncEx.

+0

Благодаря Стивену Клири за этот прекрасный пакет. – Contango

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