Блокировка ресурсов с помощью async/await

У меня есть приложение, в котором есть общий ресурс (система движения), к которому могут обращаться несколько клиентов. У меня есть отдельные операции, которым требуется доступ к системе на время перемещения и которые должны создавать исключения «Занято», если одновременно запрашиваются конфликтующие операции. У меня также есть Секвенсоры, которым необходимо получить эксклюзивный доступ к системе Движения для выполнения нескольких Операций, чередующихся с другими действиями; во время всей последовательности ни один другой клиент не должен иметь возможности запуска операций.

Я традиционно подходил к этому, используя сходство с потоком, чтобы поток мог запрашивать эксклюзивный доступ и выполнять блокирующие вызовы, соответствующие операциям. Пока поток имеет доступ, никакие другие потоки не могут использовать ресурс. Проблема, с которой я столкнулся сейчас, заключается в том, что я перешел к реализации своей системы с использованием шаблонов async/await, чтобы обеспечить более чистую реализацию секвенсора. Проблема в том, что теперь мой секвенсор не всегда работает в одном и том же потоке; активный поток может измениться во время обратных вызовов, поэтому уже не так просто определить, нахожусь ли я в допустимом контексте для продолжения выполнения операций. Следует отметить, что некоторые из операций сами состоят из ожиданий, а это означает, что как последовательности, так и отдельные операции могут охватывать несколько потоков.

Мой вопрос: кто-нибудь знает хороший шаблон для получения эксклюзивного доступа при наличии переключения потоков из-за async/await?

Для справки, несколько вещей, которые я рассмотрел:

  1. Я мог бы создать собственный SynchronizationContext, который маршалирует все вызовы секвенсора на протяжении всей последовательности обратно в один поток. Преимущество этого заключается в том, что я могу повторно использовать мой существующий код управления доступом к потокам. Недостатком является то, что это потребует выделения потока всякий раз, когда я выполняю последовательность или операцию (поскольку операции также могут охватывать несколько потоков).

  2. Создайте приобретаемый токен доступа, чтобы передать его методам операции, чтобы доказать, что вы получили доступ. Недостатком этого является раздувание методов с параметром токена.

  3. Используйте подход с токеном доступа из (2), но создайте дублирующую реализацию интерфейса для интерфейса Operations, чтобы можно было создать экземпляр оболочки с «запеченным» токеном. Это создает уродливый связующий код, но очищает код секвенсора, так что ему больше не нужно передавать токен каждому методу.


person Dan Bryant    schedule 02.10.2012    source источник


Ответы (2)


Мой вопрос: кто-нибудь знает хороший шаблон для получения эксклюзивного доступа при наличии переключения потоков из-за async/await?

Да, вы можете использовать AsyncLock, что также доступен как часть моей библиотеки AsyncEx. Если вы хотите иметь операцию типа "TryLock", вам, возможно, придется создать свой собственный примитив.

Вы теряете часть возможности выполнять проверки безопасности: нет способа проверить, имеет ли выполняющийся в данный момент поток определенный AsyncLock.

Другие варианты включают ConcurrentExclusiveSchedulerPair (о котором я пишу здесь) или поток данных TPL.

person Stephen Cleary    schedule 02.10.2012
comment
Я отмечу это как ответ для полезного чтения. В конечном итоге я создал прокси для интерфейсов, которые включают проверку доступа. Затем я могу раздать специальный одноразовый прокси-сервер с «эксклюзивным доступом», который не позволяет другим экземплярам прокси-сервера использовать систему, пока она работает. - person Dan Bryant; 06.10.2012

Здесь очень подходит SemaphoreSlim.WaitAsync. (Я нашел его в похожем вопросе).

person bohdan_trotsenko    schedule 16.11.2015