Проблема с использованием семафора для защиты очереди - PullRequest
1 голос
/ 17 февраля 2010

Я использую следующий код для ограничения использования ресурсов.

Время от времени (после 3-4 дней успешного запуска) я получаю исключение из очереди или возвращаемый объект оказывается равным нулю.

Мне интересно, ограничиваю ли я только 5 потоков для входа в этот метод Get, как это происходит.

В тех местах, где вызывается GetConnection, ReleaseConnection также определенно вызывается в блоке finally.

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

Semaphore smphSync = new Semaphore(0, 5);

Queue<IResource> resources;

private IResource GetResource()

{

    smphSync.WaitOne();

    IResource res = resources.Dequeue();

    return res;
}

private ReleaseResource(IResource res)

{

    resources.Enqueue(res);

    smphSync.Release();
}

У меня вопрос: нужно ли синхронизировать доступ к очереди (экземпляру ресурсов) с помощью блокировки / монитора?

Ответы [ 2 ]

3 голосов
/ 17 февраля 2010

Ни одна из стандартных коллекций .NET по умолчанию не поддерживает потоки. Они не могут быть доступны одновременно без какого-либо барьера памяти, препятствующего одновременному доступу.

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

Я бы также посоветовал вам выполнить тест внутри блокировки , чтобы убедиться, что в очереди все еще есть элементы для удаления, прежде чемВы пытаетесь позвонить Dequeue().Однако, поскольку я не знаю специфики работы вашего кода, я оставляю вам решать, имеет ли это отношение.

Semaphore smphSync = new Semaphore(0, 5);
Queue<IResource> resources;
private _lockObj = new object();

private IResource GetResource()
{
    smphSync.WaitOne();
    lock( _lockObj ) 
    {
        IResource res = resources.Dequeue();
        return res;
    }
}

private ReleaseResource(IResource res)
{
    lock( _lockObj )
    {
        resources.Enqueue(res);
    }
    smphSync.Release();
}
1 голос
/ 17 февраля 2010

Я добавил lock () для моего ThreadSafeQueue класса и недавно добавил метод TryDequeue (). Подробнее в этом посте . Определенно улучшены многопоточные коллизии, которые я часто видел раньше (особенно возвращал нулевой объект, когда в очереди не было нулей).

Редактировать: Проверено в методе TryDequeue () и обновлена ​​ссылка на правильный набор изменений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...