c# блокировка и прослушивание CancellationToken

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

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

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

3 ответов


взгляните на новый .NET 4.0 Framework характеристика SemaphoreSlim Класс. Он обеспечивает SemaphoreSlim.Ждать(CancellationToken) метод.

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

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

EDIT: фрагмент кода

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();
}

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
}

некоторые хорошие вещи здесь покрытие кооперативная отмена


вы могли бы использовать Monitor объект, чтобы получить немного в производительности, как указано также в MSDN:

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

подробнее

http://msdn.microsoft.com/en-us/library/system.threading.monitor.aspx

на CancellationToken предложите вам модель для совместной отмены асинхронных или длительных синхронных операций. если вы хотите использовать его с классом monitor, вам нужно структурировать код, чтобы освободить блокировку, если это запрос отмены. Вы могли бы сделать что-то ниже:

  public void ThreadSafeMethod()
        {
            var cancellationToken=new CancellationToken();
            object locker=new object();
            Monitor.Enter(locker);
            try
            {
                //your code



              if (token.IsCancellationRequested)
                 {
                   Monitor.Exit(locker);

                 }

            }
            finally
            {
                Monitor.Exit(locker);
            }
        }

или если вы хотите использовать ThrowIfCancellationRequested:

 public void ThreadSafeMethod()
        {
            var cancellationToken=new CancellationToken();
            object locker=new object();
            Monitor.Enter(locker);
            try
            {
                //your code

                cancellationToken.ThrowIfCancellationRequested(); 
            }
             catch(OperationCanceledException)
             {

             }
            finally
            {
                Monitor.Exit(locker);
            }
        }