Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# lock and listen to CancellationToken

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();
like image 939
Karsten Avatar asked Sep 14 '11 13:09

Karsten


2 Answers

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);
bool tokenCanceled = false;

try {
   try {
      // block section entrance for other threads
      semaphore.Wait(token);
   }
   catch (OperationCanceledException) {
      // The token was canceled and the semaphore was NOT entered...
      tokenCanceled = true;
   }
   // critical section code
   // ...
   if (token.IsCancellationRequested)
   {
       // ...
   }
}
finally { 
   if (!tokenCanceled)
      semaphore.Release();
}
like image 92
sll Avatar answered Sep 17 '22 13:09

sll


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

like image 37
Peter Kelly Avatar answered Sep 16 '22 13:09

Peter Kelly