Динамически уменьшить емкость семафора

Я пытался использовать семафор для контроля количества запросов, которые может обработать моя служба. т.е.

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

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

У меня есть 2 проблемы, которые я не знаю, как решить. Я использовал подобные хаки для решения этих проблем, но мне интересно, есть ли лучший способ

  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 я хочу убедиться, что вся обработка запроса завершена, прежде чем я избавлюсь от семафора, иначе вызов s.Release() в моем ProcessRequest() вызовет ObjectDisposedException, поэтому я сделал следующее

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

Обратите внимание, что я много раз использовал цикл для ручного ожидания. Это очень медленно, если емкость большая. Есть лучший способ это сделать? Для семафора есть Release (int count), почему нет Wait (int count)?


person user1763590    schedule 06.12.2013    source источник
comment
Я отредактировал ваш заголовок. См. Должны ли вопросы включать «теги» в свои заголовки?, если нет единого мнения, не следует.   -  person John Saunders    schedule 06.12.2013


Ответы (1)


Что я, вероятно, сделаю, так это когда настройка будет сделана, замените ваш экземпляр семафора новым семафором с желаемой емкостью и назначьте всю будущую работу этому семафору. Существующий и теперь разыменованный семафор не будет удален сборщиком мусора до тех пор, пока все потоки не будут ссылаться на него, поэтому это должно быть безопасно, если вы назначаете переменную семафора локально каждому потоку.

person Haney    schedule 06.12.2013
comment
Спасибо, но это решит только мою первую проблему, а не вторую? Кроме того, если мы заменяем семафор, нам нужно сделать его изменчивым, верно? это сделало бы обычный запрос медленнее в пользу этих более редких операций... вот почему я не делал замену в первую очередь - person user1763590; 06.12.2013
comment
Подумав об этом немного больше, кажется, что даже volatile недостаточно. Нам нужно установить вокруг него блокировки, потому что создание нового семафора требует времени, и во время создания исходное состояние семафора может измениться... так что я думаю, что это решение не сработает для меня. - person user1763590; 07.12.2013
comment
Вам не нужно запирать его. Это концепция нетерпеливой оценки. Если я назначу переменную локально в начале моего метода, другой поток может изменить исходную ссылку, которую я назначил, и моя локальная переменная останется той же ссылкой. Здесь вам понадобится volatile, чтобы компилятор не оптимизировал назначение. Что касается проблемы № 2 (раскрутка), вы не должны вызывать Dispose ДО тех пор, пока не будут выполнены все ваши экземпляры. Рассматривайте свою архитектуру как Dispose для неуправляемых ресурсов, а не для управляемой бизнес-логики. - person Haney; 07.12.2013