c#锁定并听取CancellationToken

我想使用锁或类似的同步来保护关键部分。 同时我想听一个CancellationToken。

现在我正在使用这样的互斥锁,但是互斥锁并没有那么好的性能。 我可以使用任何其他同步类(包括新的.Net 4.0)而不是互斥锁吗?

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

看一下新的.NET 4.0 FrameworkfunctionSemaphoreSlim类 。 它提供了SemaphoreSlim.Wait(CancellationToken)方法。

阻止当前线程,直到它可以进入SemaphoreSlim,同时观察CancellationToken

从某些角度来看,在这种简单的情况下使用Semaphore可能是一种开销,因为最初它的设计目的是为多个线程提供访问权限,但也许您可能会发现它很有用。

编辑:代码片段

 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类启动任务,该类将聚合所有exception以供您在调用线程上处理。

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

这里有一些好的东西,包括合作取消

您可以使用Monitor对象获得一些性能,如MSDN中所述:

尽管可以将互斥锁用于进程内线程同步,但通常首选使用Monitor,因为监视器是专门为.NET Framework设计的,因此可以更好地利用资源

有关更多信息

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

CancellationToken为您提供了协同取消异步或长时间运行同步操作的模型。 如果要将它与监视器类一起使用,则必须构造代码以在取消请求时释放锁定。 你可以做以下事情:

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