【问题标题】:Why is semaphore not released when given cancellationToken is cancelled为什么取消给定的cancellationToken时没有释放信号量
【发布时间】:2021-12-28 20:32:43
【问题描述】:

SemaphoreSlim 有一个WaitAsync() 方法,它采用CancellationToken。我希望在取消该令牌时释放信号量,但情况似乎并非如此。考虑下面的代码:

var tokenSource = new CancellationTokenSource();
var semaphore = new SemaphoreSlim(1, 1);
// CurrentCount is 1
await semaphore.WaitAsync(tokenSource.Token);
// CurrentCount is 0 as we'd expect

tokenSource.Cancel(); // Token is cancelled

for(var i = 0; i< 10; i++)
{
    var c = semaphore.CurrentCount; // Current count remains 0, even though token was cancelled
    await Task.Delay(1000);
}

即使在令牌被取消后,CurrentCount 仍为 0。我猜这是设计使然,但如果在取消令牌时不释放信号量,CancellationToken 究竟是用来做什么的?谢谢!

【问题讨论】:

  • CancellationToken 用于取消等待。与获取的信号量没有业务往来。在您的代码中,当您调用 WaitAsync 时,它会立即返回,正如您所注意到的,因为允许 1 个线程拥有信号量。如果你用 0 个当前允许的线程初始化信号量,那么等待调用将真正等待。现在,如果您在其他线程上调用 tokenSource.Cancel(),则等待返回,因为它在等待信号量期间监视您的令牌。
  • @cly 哦,这很有道理!非常感谢..

标签: c# .net semaphore


【解决方案1】:

背景

SemaphoreSlim Class

代表 Semaphore 的轻量级替代方案,它限制了 可以访问资源或资源池的线程数 同时。

...

每次线程进入信号量时计数递减,并且 每次线程释放信号量时递增。要输入 信号量,线程调用 Wait 或 WaitAsync 重载之一。到 释放信号量,它调用 Release 重载之一。当。。。的时候 计数达到零,随后对等待方法之一的调用阻塞 直到其他线程释放信号量。如果有多个线程 阻塞,没有保证的顺序,例如先进先出或后进先出,即 控制线程何时进入信号量。

SemaphoreSlim.CurrentCount

获取可以进入 SemaphoreSlim 的剩余线程数 对象。

...

CurrentCount 属性的初始值由调用设置 SemaphoreSlim 类构造函数。每次调用都会递减 Wait 或 WaitAsync 方法,每次调用 发布方式。

也就是说,每次你输入一个信号量,你就会减少可以进入的剩余线程数。如果输入成功,则计数递减。

调用正在等待的 CancellationToken 只会对等待 WaitAsync 的线程产生影响,或者如果您再次尝试等待令牌。

回答这个问题,CancellationToken只针对等待中的WaitAsync,它不影响有多少线程可以进入SemaphoreSlim

此外

我认为真正相关的问题是,您是否需要发布已取消的SemephoreSlim!答案是否定的。一个等待的SemephoreSlim 无论如何都没有成功进入或影响计数,它正在等待,因为没有线程允许。

最后,您是否需要释放因超时过载而超时的SemephoreSlim。答案是,这个方法返回一个bool是否进入成功,需要检查返回值来判断是否需要释放。

趣事

这就是为什么您不将等待放在释放苗条的 try finally 模式中的确切原因。

// this can throw, or timeout
var result = await semaphore.WaitAsync(someTimeOut, tokenSource.Token);

if(!result)
  return;

try
{
   // synchronized work here
}
finally
{
   semaphore.Release();
}

不恰当的释放会导致各种各样的问题,迟早会导致另一个异常,因为你超过了最大计数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-12
    • 1970-01-01
    • 2020-12-01
    • 1970-01-01
    • 2015-03-06
    相关资源
    最近更新 更多