Синхронизация потоков
От: Polosaty Беларусь  
Дата: 03.04.02 15:27
Оценка:
Пытаюсь создать класс безопасной в многопоточной среде очереди (например, указателей на что-нибудь). Открытый интерфейс класса должен выглядеть примерно так:
class CMyQueue  
{
public:
    CMyQueue(BOOL Start = TRUE);
    virtual ~CMyQueue();

    void Start();
    void StopAndEmpty();
    BOOL IsEmpty() const;
    void Push(void* Ptr);
    void* Pop();
};

Прошу не обращать внимания на void*, это для упрощения примера. Понятно, что тут может быть конкретный тип, или класс может быть параметрическим.
Дополнительные требования:
1) Очередь организуется на односвязном списке, хранятся только указатели на голову (для извлечения элементов) и на хвост (для постановки элементов в очередь). Соответственно количество элементов ничем не ограничено.
2) Эта очередь должна уметь прекращать обслуживание потоков-клиентов. Т.е., например, если функционирование очереди остановлено, при вызове потоком метода Push() передаваемый в метод Ptr не должен ставиться в очередь.
3) Для запуска-остановки очереди используются методы Start() и StopAndEmpty(). Параметр, передаваемый в конструктор, сообщает, должна ли очередь начать функционирование немедленно после создания или ожидать вызова метода Start().
4) Метод StopAndEmpty(), кроме останова, должен производить очистку очереди.
5) Специфика условий, в которых эта очередь будет использоваться, такова, что потоки, которые делают Pop, абсолютно бОльшую часть времени будут ждать в методе Pop появления очередного элемента. Соответственно стоит задача корректного завершения этих потоков. Момент завершения не должен быть связан с мометом очистки очереди, т.к. очередь может быть остановлена и очищена, а затем вновь запущена. Потоки после остановки и очистки очереди должны спать в методе Pop. Потеря элементов при остановке и очистке очереди допустима. В принципе, для завершения ждущих в методе Pop() потоков приемлемым может быть решениие передавать в метод Pop() параметр — например, хэндл события, которое будет устанавливаться родительским потоком, когда нужно будет завершить спящие потоки.

Вот я и голову сломал, как это все синхронизировать. Сначала пытался использовать критическую секцию, в которой сосредоточил весь код, модифицирующий указатели на голову и хвост, и событие для пробуждения потока в Pop, если в очереди есть элемент. Но с помощью этого "аппарата" задача, похоже, не решается. Потом думал пользовать вместо события семафор с достаточно большим ограничением на количество ресурсов, но текущий счетчик ресурсов семафора невозможно сбросить в 0 при очистке очереди. В конце концов родилась идея использовать вместо критической секции и события мьютекс и событие. Тогда доступ к модификации головы и хвоста очереди можно защищать мьютексом, а событие с автосбросом будет в сигнальном состоянии, когда очередь не пуста. Тогда SetEvent нужно будет делать в методе Push при постановке элемента в очередь, и в методе Pop после того, как очередной поток вынул элемент, если после этого очередь не стала пустой. А в начале метода Pop ждать и мьютекс, и событие. Завершение же потоков, спящих в Push, можно организовать с помощью помещения в очередь указателей на какую-нибудь специальную информацию, тогда поток, вынувший такой элемент, проанализирует его и поймет, что должен завершиться.
И, наконец, вопросы: есть ли у кого-нибудь соображения, как решить эту задачу действительно красиво и эффективно? Насколько мой последний предложенный вариант действительно будет работать, а то я уже задолбался переделывать код?
Если кто-нибудь дочитает до этого места, то спасибо уже и на этом :) .
З.Ы. Рихтера уже до дыр зачитал. Там во всех примерах чего-нибудь не хватает для моего случая.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.