Глава.30 .Гибридные.конструкции.синхронизации.потоков
у одного из наших клиентов. Затем я продал патентные права Microsoft. В 2009 году
Патентное управление выдало патент номер 7 603 502.
Класс
SemaphoreSlim
реализует эту идею в своем методе
WaitAsync
. Сигнатура
самой сложной перегруженной версии этого метода выглядит так:
public Task WaitAsync(Int32 millisecondsTimeout,
CancellationToken cancellationToken);
С ней вы можете синхронизировать доступ к ресурсу в асинхронном режиме
(то есть без блокирования каких-либо потоков).
private static async Task
AccessResourceViaAsyncSynchronization(SemaphoreSlim asyncLock) {
// TODO: Разместите здесь любой код на ваше усмотрение...
await asyncLock.WaitAsync(); // Запрос монопольного доступа к ресурсу.
// Когда управление попадает в эту точку, мы знаем, что никакой другой
// поток не обращается к ресурсу.
// TODO: Работа с ресурсом (в монопольном режиме)...
// Завершив работу с ресурсом, снимаем блокировку, чтобы ресурс
// стал доступным для других потоков.
asyncLock.Release();
// TODO: Разместите здесь любой код на ваше усмотрение...
}
Метод
WaitAsync
класса
SemaphoreSlim
чрезвычайно полезен, но, конечно, он
реализует семантику семафора. Обычно объект
SemaphoreSlim
создается со счет-
чиком 1, что обеспечивает взаимоисключающий доступ к защищаемому ресурсу.
Таким образом, реализуемое поведение сходно с тем, которое достигается при
использовании
Monitor
— не считая того, что
SemaphoreSlim
не предоставляет
семантики рекурсии и владения потоками (впрочем, это хорошо).
А что делать с семантикой чтения/записи? В .NET Framework входит класс
Con currentExclusiveSchedulerPair
, который выглядит примерно так:
public class ConcurrentExclusiveSchedulerPair {
public ConcurrentExclusiveSchedulerPair();
public TaskScheduler ExclusiveScheduler { get; }
public TaskScheduler ConcurrentScheduler { get; }
// Другие методы не показаны
}
Экземпляр этого класса содержит два объекта
TaskScheduler
, которые со-
вместно реализуют семантику чтения/записи при планировании заданий. Все за-
дания, запланированные с использованием
ExclusiveScheduler
, выполняются по
одному — при отсутствии выполняемых задач, запланированных с использованием
ConcurrentScheduler
. И конечно, все задачи, запланированные с использованием
ConcurrentScheduler
, могут выполняться одновременно — при отсутствии выпол-
885
Асинхронная.синхронизация
няемых заданий, запланированных
ExclusiveScheduler
. Пример использования
класса
ConcurrentExclusiveSchedulerPair
представлен ниже:
private static void ConcurrentExclusiveSchedulerDemo() {
var cesp = new ConcurrentExclusiveSchedulerPair();
var tfExclusive = new TaskFactory(cesp.ExclusiveScheduler);
var tfConcurrent = new TaskFactory(cesp.ConcurrentScheduler);
for (Int32 operation = 0; operation < 5; operation++) {
var exclusive = operation < 2; // Для демонстрации создаются
// 2 монопольных и 3 параллельных задания
(exclusive ? tfExclusive : tfConcurrent).StartNew(() => {
Console.WriteLine("{0} access", exclusive ? "exclusive" : "concurrent");
// TODO: Здесь выполняется монопольная запись или параллельное чтение...
});
}
}
К сожалению, .NET Framework не предоставляет асинхронных средств блоки-
ровки с семантикой чтения/записи. Впрочем, я создал такой класс, который назвал
AsyncOneManyLock
. Он используется по тем же принципам, что и
SemaphoreSlim
:
private static async Task
AccessResourceViaAsyncSynchronization(AsyncOneManyLock asyncLock) {
// TODO: Здесь выполняется любой код...
// Передайте OneManyMode.Exclusive или OneManyMode.Shared
// в зависимости от нужного параллельного доступа
await asyncLock.AcquireAsync(OneManyMode.Shared); // Запросить общий доступ
// Когда управление передается в эту точку, потоки, выполняющие
// запись в ресурс, отсутствуют; другие потоки могут читать данные
// TODO: Чтение из ресурса...
// Завершив работу с ресурсом, снимаем блокировку, чтобы ресурс
// стал доступным для других потоков.
asyncLock.Release();
// TODO: Здесь выполняется любой код...
}
Ниже приведен код моей реализации
AsyncOneManyLock
.
public enum OneManyMode { Exclusive, Shared }
public sealed class AsyncOneManyLock {
#region Lock code
private SpinLock m_lock = new SpinLock(true); // Не используем
// readonly с SpinLock
private void Lock() { Boolean taken = false; m_lock.Enter(ref taken); }
private void Unlock() { m_lock.Exit(); }
#endregion
#region Lock state and helper methods
продолжение
886
Do'stlaringiz bilan baham: |