c# lock and listen to CancellationToken

2019-04-06 14:59发布

问题:

I want to use lock or a similar synchronization to protect a critical section. At the same time I want to listen to a CancellationToken.

Right now I'm using a mutex like this, but mutex doesn't have as good performance. Can I use any of other synchronization classes (including the new .Net 4.0) instead of the mutex?

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

回答1:

Take a look at the new .NET 4.0 Framework feature SemaphoreSlim Class. It provides SemaphoreSlim.Wait(CancellationToken) method.

Blocks the current thread until it can enter the SemaphoreSlim, while observing a CancellationToken

From some point of view using Semaphore in such simple case could be an overhead because initially it was designed to provide an access for multiple threads, but perhaps you might find it useful.

EDIT: The code snippet

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:

private object _lockObject = new object();

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

Calling Cancel() on a token will result in the ThrowIfCancellationRequested() being invoked as that was what is hooked up to the Register callback. You can put whatever cancellation logic you want in here. This approach is great because you can cancel blocking calls by forcing the conditions that will cause the call to complete.

ThrowIfCancellationRequested throws a OperationCanceledException. You need to handle this on the calling thread or your whole process could be brought down. A simple way of doing this is by starting your task using the Task class which will aggregate all the exceptions up for you to handle on the calling thread.

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

Some good stuff here covering co-operative cancellation



回答3:

you could use Monitor object to gain a bit in performance as it is stated also in MSDN:

Although a mutex can be used for intra-process thread synchronization, using Monitor is generally preferred, because monitors were designed specifically for the .NET Framework and therefore make better use of resources

For more info

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

the CancellationToken offer you a model for cooperative cancellation of asynchronous or long-running synchronous operations. if you want to use it with the monitor class you have to structure the code to release the lock if the is a cancellation request. You could do something the below:

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

or if you want use 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);
            }
        }