c# 锁定并监听CancellationToken

发布于 2024-12-04 16:14:08 字数 254 浏览 0 评论 0原文

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

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

如果没有你 2024-12-11 16:14:08

了解新的 .NET 4.0 Framework 功能 SemaphoreSlim 类。它提供了 SemaphoreSlim.Wait(CancellationToken) 方法。

阻塞当前线程,直到它可以进入SemaphoreSlim,而
观察 CancellationToken

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

编辑:代码片段

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

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();
}
驱逐舰岛风号 2024-12-11 16:14:08
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
}

这里介绍了合作取消的一些好东西

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

凉城 2024-12-11 16:14:08

您可以使用带有超时的 Monitor.TryEnter 来等待锁定并定期检查是否取消。

private bool TryEnterSyncLock(object syncObject)
{
    while(!Monitor.TryEnter(syncObject, TimeSpan.FromMilliseconds(100)))
    {
        if (cts_.IsCancellationRequested)
            return false;
    }

    return true;
}

请注意,我不会在高争用情况下建议这样做,因为它会影响性能。我将使用它作为防止死锁的安全机制,以防您无法使用 SemaphoreSlim,因为它与 Monitor.Enter 具有不同的相同线程重入语义。

返回true后,必须使用Monitor.Exit释放对syncObject的锁定。

You can use Monitor.TryEnter with timeout to wait for the lock and check periodically for cancellation.

private bool TryEnterSyncLock(object syncObject)
{
    while(!Monitor.TryEnter(syncObject, TimeSpan.FromMilliseconds(100)))
    {
        if (cts_.IsCancellationRequested)
            return false;
    }

    return true;
}

Note that I would not recommend this in high contention situations as it can impact performance. I would use it as a safety mechanism against deadlocks in case you cannot use SemaphoreSlim as it has different same thread re-entrancy semantics than Monitor.Enter.

After returning true, lock on syncObject has to be released using Monitor.Exit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文