c # блокировка и прослушивание CancellationToken - PullRequest
8 голосов
/ 14 сентября 2011

Я хочу использовать блокировку или аналогичную синхронизацию для защиты критического раздела.В то же время я хочу прослушать CancellationToken.

Сейчас я использую мьютекс, подобный этому, но мьютекс не имеет такой хорошей производительности.Могу ли я использовать любой другой класс синхронизации (включая новый .Net 4.0) вместо мьютекса?

WaitHandle.WaitAny(new[] { CancelToken.WaitHandle, _mutex});
CancelToken.ThrowIfCancellationRequested();

Ответы [ 2 ]

12 голосов
/ 14 сентября 2011

Взгляните на новую .NET 4.0 Framework функцию SemaphoreSlim Class .Он обеспечивает SemaphoreSlim.Wait (CancellationToken) метод.

Блокирует текущий поток до тех пор, пока он не сможет войти в SemaphoreSlim, соблюдая CancellationToken

Снекоторые точки зрения, использующие семафор в таком простом случае, могут быть непроизводительными, поскольку изначально он был разработан для обеспечения доступа к нескольким потокам, но, возможно, вы могли бы найти его полезным.

РЕДАКТИРОВАТЬ: Фрагмент кода

CancellationToken token = new CancellationToken();            
SemaphoreSlim semaphore = new SemaphoreSlim(1,1);

try {
   // block section entrance for other threads
   semaphore.Wait(token);

   // critical section code
   // ...
   if (token.IsCancellationRequested)
   {
       // ...
   }
}
finally { 
   semaphore.Release();
}
2 голосов
/ 14 сентября 2011
private object _lockObject = new object();

lock (_lockObject)
{  
   // critical section  
   using (token.Register(() => token.ThrowIfCancellationRequested())
   {
       // Do something that might need cancelling. 
   }
}

Вызов Cancel() на токене приведет к вызову ThrowIfCancellationRequested(), так как он был подключен к обратному вызову Register.Здесь вы можете поместить любую логику отмены, какую захотите.Этот подход хорош, потому что вы можете отменить блокировку вызовов, вызвав условия, которые вызовут завершение вызова.

ThrowIfCancellationRequested создает исключение OperationCanceledException.Вы должны обработать это в вызывающем потоке, иначе весь ваш процесс может быть остановлен.Простой способ сделать это - запустить задачу с помощью класса Task, который объединит все исключения, которые вы сможете обработать в вызывающем потоке.

try
{
   var t = new Task(() => LongRunningMethod());
   t.Start();
   t.Wait();
}
catch (AggregateException ex)
{
   ex.Handle(x => true); // this effectively swallows any exceptions
}

Некоторые хорошие вещи здесь , охватывающие совместную отмену

...