【问题标题】:Dynamically reduce Semaphore's capacity动态减少信号量的容量
【发布时间】:2013-12-06 00:59:53
【问题描述】:

我一直在尝试使用信号量来控制我的服务可以处理的请求数量。即。

class Service : IDisposable {
    SemaphoreSlim s = new SemaphoreSlim(InitialCapacity);

    ....
    async void ProcessRequest() {
        await s.WaitAsync();
        try {
             ......
        } finally {
            s.Release();
        }
    }
}

我遇到了 2 个问题,我不知道如何解决。我一直在使用类似的 hack 来解决这些问题,但我想知道是否有更好的方法

  1. 我希望能够动态改变我的服务类的容量,所以我有这样的东西。

    void ChangeCapacity(int newCapacity) {
        int extraRequestsCount = newCapacity - oldCapacity;
        if (extraRequestsCount > 0) {
            s.Release(extraRequestsCount);
        }
        else if (extraRequestsCount < 0) {
            for (int i = 0; i < -extraRequestsCount; i++) {
                s.WaitAsync(); // try to steal some resources, over time...
            }
        }
    }
    
  2. 在 dispose 方法中,我想确保所有请求处理在我处理信号量之前完成,否则我的 ProcessRequest() 中的 s.Release() 调用会抛出 ObjectDisposedException,所以我做了以下操作

    public void Dispose() {
        if (s!= null) {
            for (int i = 0; i < oldCapacity; i++) {
                s.Wait();
            }
            s.Dispose();
        }
    }
    

请注意,我一直在使用循环手动等待很多次。如果容量很大,这真的很慢。有一个更好的方法吗?信号量有一个 Release(int count) 为什么没有 Wait(int count)?

【问题讨论】:

标签: c# concurrency thread-safety locking semaphore


【解决方案1】:

我可能会做的是在进行调整时,将您的信号量实例替换为具有所需容量的新信号量,并将所有未来的工作分配给该信号量。在所有线程完成对它的引用之前,不会对现有的和现在取消引用的信号量进行垃圾收集,因此只要您将信号量变量本地分配给每个线程,这样做应该是安全的。

【讨论】:

  • 谢谢,但这只能解决我的第一个问题而不是第二个问题?另外,如果我们要换掉 Semaphore,我们需要让它 volatile 对吗?这将使正常请求变慢,有利于这些罕见的操作......这就是为什么我没有首先进行交换
  • 想了想,好像连 volatile 都不够。我们应该需要在它周围加锁,因为创建新信号量需要时间,并且在创建过程中原始信号量状态可能会改变......所以我想这个解决方案对我来说根本不起作用
  • 您不需要锁定它。这就是热切评价的概念。如果我在方法开始时本地分配变量,另一个线程可以更改我分配的原始引用,并且我的本地变量保持相同的引用。您希望在此处使用 volatile 以确保编译器不会优化分配。就问题 #2(spindown)而言,您不应该调用 Dispose 直到您的所有实例都完成。考虑您的架构,因为 Dispose 用于非托管资源,而不是托管业务逻辑。
猜你喜欢
  • 1970-01-01
  • 2021-01-18
  • 2017-05-06
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
相关资源
最近更新 更多