在调用 WaitAsync 方法时如何正确释放 SemaphoreSlim?

3

SemaphoreSlim的文档中说:“只有在所有其他操作都已完成时才应使用Dispose”。

以下类应如何调整,以便线程B在线程A等待Async()时可以调用Dispose()。当调用Dispose()时,Async()必须抛出ObjectDisposedException。

class A
{
   SemaphoreSlim _sem = new SemaphoreSlim(0);
   public Task Async() { return _sem.WaitAsync(); }
   public void Dispose() { _sem.Dispose(); }
}

在这种情况下,为什么您希望线程B调用Dispose()呢? - Jon Hanna
Dispose被调用作为取消所有挂起请求和释放资源的手段。 - Tom Deseyn
1
但是 Dispose() 没有任何理由去取消任何操作。如果你想要取消,那么应该在传递给 WaitAsync 和同一批请求中的其他异步方法的 CancellationToken 上进行取消。 - Jon Hanna
1个回答

0
以下类应如何调整,以便线程B在等待Async()时可以调用Dispose()?
不应该这样做。正如您引用的文档所说,如果所有操作都没有完成,则不应调用Dispose()。如果仍有一个线程正在等待WaitAsync(),则该操作尚未完成,因此不应调用Dispose()。
从评论中:
Dispose被称为取消所有挂起请求并释放资源的手段。
这是一种不明智的取消请求的方式。
当然,如果您执行某些操作使对象处于无效状态,它可能会导致其他线程中的异常,这可能会导致它们中止并因此放弃工作。
但是,并没有保证他们应该这样做,而且也有很大的机会,他们会编写代码以不期望在这个点上出现这样的异常,并适当地清理。
如果要取消,请进行正常取消。创建任务如下:
public Task Async(CancellationToken cancellationToken)
{
  return _sem.WaitAsync(cancellationToken);
}

使用 cancellationToken 进行取消操作。


Jon Hanna,HttpClient.Dispose 是一个取消挂起请求并释放资源的方法示例。它在内部使用 cancellationToken。您能否给出一个代码示例,该示例在保持类 A 的接口的同时处理 semaphore 的 Dispose 并使用 cancellationToken? - Tom Deseyn
"HttpClient.Dispose是一个取消挂起请求并释放资源的方法的例子。它实际上是释放资源,因为它是一个HTTP客户端,这意味着取消挂起的请求。" "你能否给出一个代码示例,该示例Dispose了semaphore并在保持类A的接口的同时使用cancellationToken?" 不行,接口未定义为允许取消操作。如果方法调用既不接受令牌,也没有一种方法可以为所有已调用的方法设置令牌,则未定义为支持取消操作。 - Jon Hanna
如果您可以添加到接口但不能删除,则可以添加一个属性或构造函数参数来设置一个令牌,以供类的所有方法使用,可能默认为 CancellationToken.None - Jon Hanna
但 CancellationToken 也无法解决这个问题。即使你调用 {_cancellationTokenSource.Cancel(); _sem.Dispose(); },仍然会有一些 _sem.WaitAsync(cancellationToken) 在黑洞中等待直到进程结束!最好的处理 Class A 的方法是什么? - Mohammad Nikravan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接