SRC: Универсальный блокировщик типа читатели/писатель
От: SergH Россия  
Дата: 18.09.02 00:05
Оценка:
Здравствуйте.

Понадобилось мне в нескольких независимых местах блокировать разные ресурсы на чтение/запись. Написал такую штуку:

#ifndef LOCKER_H
#define LOCKER_H

template<int magic>
class Locker
{
public:
    Locker(bool write) 
    {
        init();

        m_write = write;

        if (!m_write)
        {
            WaitForSingleObject(mutex, INFINITE);

            if (!readers)
            {
                ResetEvent(event);
            }

            InterlockedIncrement(&readers);

            ReleaseMutex(mutex);
        }
        else
        {
            HANDLE h[] = {event, mutex};

            WaitForMultipleObjects(2, h, TRUE, INFINITE);
        }
    }

    ~Locker() 
    {
        WaitForSingleObject(mutex, INFINITE);

        if (!m_write)
        {
            InterlockedDecrement(&readers);

            if (!readers)
            {
                SetEvent(event);
            }
        }
        else
        {
            ReleaseMutex(mutex);
        }

        ReleaseMutex(mutex);
    };

private:

    void init()
    {
        if (!first) return;
        first = false;
        mutex = CreateMutex(NULL, FALSE, NULL);
        event = CreateEvent(NULL, TRUE, TRUE, NULL);
    }

    static bool   first;
    static HANDLE mutex;
    static HANDLE event;
    static LONG   readers;

    bool m_write;
};

template<int magic>
bool Locker<magic>::first = true;

template<int magic>
HANDLE Locker<magic>::mutex = NULL;

template<int magic>
HANDLE Locker<magic>::event = NULL;

template<int magic>
LONG Locker<magic>::readers = 0;

#endif //LOCKER_H


Пример использования:

typedef Locker<123> Resource1Locker;
typedef Locker<456> Resource2Locker;

.....

void func1()
{
    Resource1Locker lock(false);
    // теперь первый ресурс можно безболезненно читать, его не изменят до конца функции
}

void func2()
{
    Resource2Locker lock(true);
    // теперь второй ресурс можно безболезненно изменять, его не будут читать до конца функции
}


Просто, понятно, работает (!)... Компилится в VC6 (!!). Единственное слабое место — код инициализации . Но тут уж ничего не поделаешь (если кто-то знает способ — опубликуйте, буду благодарен).. Параноикам могу предложить перед началом создовать по штуке объектов каждого класса, тогда этой проблемы не будет. Лично я не настолько параноик, предпочитаю надеяться на лучшее
Делай что должно, и будь что будет
Re: Универсальный блокировщик типа читатели/писатель
От: Алекс Россия http://wise-orm.com
Дата: 18.09.02 00:55
Оценка:
Здравствуйте SergH, Вы писали:

SH>Здравствуйте.


SH>Понадобилось мне в нескольких независимых местах блокировать разные ресурсы на чтение/запись. Написал такую штуку:


че то мне не очень нравится код. Например вот это

SH>
...
SH>        else
SH>        {
SH>            HANDLE h[] = {event, mutex};

SH>            WaitForMultipleObjects(2, h, TRUE, INFINITE);
SH>        }
SH>    }
SH>


здесь заняли мутекс. А когда освобождать будем? В деструкторе? А в деструкторе мы его опять ждем! Баг, однако!

SH>
SH>    ~Locker() 
SH>    {
SH>        WaitForSingleObject(mutex, INFINITE);

SH>        if (!m_write)
SH>        {
SH>            InterlockedDecrement(&readers);

SH>            if (!readers)
SH>            {
SH>                SetEvent(event);
SH>            }
SH>        }
SH>        else
SH>        {
SH>            ReleaseMutex(mutex);
SH>        }

SH>        ReleaseMutex(mutex);
SH>    };
...
SH>


На сколько я помню, у Рихтера примерно так было:

//Запись данных
WaitForMultipleObjects(hRead,hWrite);
...        //пишем                
ReleaseMutex(hWrite);                

//Чтение данных
WaitForSingleObject(hWrite);
ResetEvent(hRead);                    
ReleaseMotex(hWrite);                    
...        //читаем
SetEvent(hRead);


только там еще семафор использовался (довольно криво).
Re[2]: Универсальный блокировщик типа читатели/писатель
От: SergH Россия  
Дата: 18.09.02 01:05
Оценка:
Здравствуйте Алекс, Вы писали:

А>че то мне не очень нравится код. Например вот это


SH>>
А>...
SH>>        else
SH>>        {
SH>>            HANDLE h[] = {event, mutex};

SH>>            WaitForMultipleObjects(2, h, TRUE, INFINITE);
SH>>        }
SH>>    }
SH>>


А>здесь заняли мутекс. А когда освобождать будем? В деструкторе? А в деструкторе мы его опять ждем! Баг, однако!


Не, не баг, а фича. Фича mutex'ов. Они поддерживают счетчик, сколько раз занял, столько и отпустил. Если ты внимательно посмотришь на деструктор, то увидишь, что "читатели" освобождают mutex один раз, т.к. занимали его один раз, а "писатели" — два раза.

Занимать mutex в деструкторе нужно для того, чтобы уход последнего читателя был атомарной операцией. Т.е. что бы во время этого другие читател не входили, а то плохо может получиться.
Делай что должно, и будь что будет
Re[3]: Универсальный блокировщик типа читатели/писатель
От: Алекс Россия http://wise-orm.com
Дата: 18.09.02 01:25
Оценка:
Здравствуйте SergH, Вы писали:

SH>Здравствуйте Алекс, Вы писали:


[]

SH>Не, не баг, а фича. Фича mutex'ов. Они поддерживают счетчик, сколько раз занял, столько и отпустил. Если ты внимательно посмотришь на деструктор, то увидишь, что "читатели" освобождают mutex один раз, т.к. занимали его один раз, а "писатели" — два раза.


SH>Занимать mutex в деструкторе нужно для того, чтобы уход последнего читателя был атомарной операцией. Т.е. что бы во время этого другие читател не входили, а то плохо может получиться.


В смысле между InterlockedDecrement() и SetEvent()? Может быть. Под утро конечно плохо соображается.
И все равно. Плохо что мы не контролируем сами время сброса. Может быть в этой функции (где создается Locker) я еще чего-нибуlь хочу поделать, длительное. Может очень длительное.
Re[4]: Универсальный блокировщик типа читатели/писатель
От: SergH Россия  
Дата: 18.09.02 01:36
Оценка:
Здравствуйте Алекс, Вы писали:

А>В смысле между InterlockedDecrement() и SetEvent()?


Угу.

А>И все равно. Плохо что мы не контролируем сами время сброса. Может быть в этой функции (где создается Locker) я еще чего-нибуlь хочу поделать, длительное. Может очень длительное.


Тогда так:

void func()
{
    // длительное...
    {
         // и здесь длительное...
         SomeLoker lock(false);
         // обращаемся к ресурсу...
    }
    // опять длительное...
}


Да и вообще, добавить сюда функции типа lock/unlock — элементарно. Просто мне не нужно, было, я и не стал...
Делай что должно, и будь что будет
Re: SRC: Универсальный блокировщик типа читатели/писатель
От: Аноним  
Дата: 18.09.02 09:05
Оценка:
Здравствуйте SergH. Поскольку Вы явно используете этот код в рамках одного процесса, то не проще и понятнее ли пользоваться критическими секциями ? Например, вот так:

class CriticalSection
{
    friend class WorkWithCriticalSection;
    CRITICAL_SECTION h;
public:
    CriticalSection()
    {
        InitializeCriticalSection(&h);
    }
    ~CriticalSection()
    {
        DeleteCriticalSection(&h);
    }
};

class WorkWithCriticalSection
{
    CriticalSection* cs;
public:
    WorkWithCriticalSection(CriticalSection* acs)
    {
        cs = acs;
        EnterCriticalSection(&cs->h); 
    }
    ~WorkWithCriticalSection()
    {
        LeaveCriticalSection(&cs->h);
    }
};


Пример использования:


CriticalSection cs;

void some_impotant_function()
{
WorkWithCriticalSection o(&cs);
...
ресурс заблокирован
...
}

void some_critical_function()
{
WorkWithCriticalSection o(&cs);
...
ресурс заблокирован
...
}
Re[2]: SRC: Универсальный блокировщик типа читатели/писатель
От: SergH Россия  
Дата: 18.09.02 12:14
Оценка:
Здравствуйте Аноним, Вы писали:

А>Поскольку Вы явно используете этот код в рамках одного процесса, то не проще и понятнее ли пользоваться критическими секциями ? Например, вот так:


[skip]

Я начинал с чего-то подобного. Но такой подход не позволяет отделить читателей от писателей, т.е. несколько читателей не могут читать одновременно. Мне это не подходит.

Заменить mutex на критическую секцию нельзя, т.к. её не передать в WaitForMultipleObjects, а захватывать объекты по одному в данном случае тоже нельзя. Иначе может получиться так, что писатель в конструкторе захватил критическую секцию, и ждёт события, а читатель в деструкторе ждёт критическую секцию...
Делай что должно, и будь что будет
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.