Вопрос об использовании CRITICAL_SECTION - PullRequest
2 голосов
/ 24 марта 2011

Учитывая следующий код,

class MyCriticalSection : protected CRITICAL_SECTION
{
public:
    MyCriticalSection()
    {
        InitializeCriticalSection(this);
        EnterCriticalSection(this);
    }
    ~MyCriticalSection()
    {
        LeaveCriticalSection(this);
        DeleteCriticalSection(this);
    }
};

class MyClass
{
public:
    MyClass() : m_Int(0) {}

    int GetNum() 
    { 
        MyCriticalSection myCriticalSectionA;  // LockA

        return m_Int; 
    }

    int SetNum(int i)
    {
        MyCriticalSection myCriticalSectionB;  // LockB

        m_Int = i;
    }
protected:
    int m_Int;
};

Я предполагаю, что приведенный выше дизайн верен, пожалуйста, исправьте меня, если вы обнаружите какие-либо ошибки. Использование LockA имеет две возможности: 1> предотвращает одновременный вызов функции GetNum более чем одним потоком. 2> предотвращает одновременный доступ функций m_Int к функциям GetNum и SetNum.

Вот мой вопрос: Переменные myCriticalSectionA и myCriticalSectionB являются двумя различными экземплярами класса MyCriticalSection. Почему они могут сотрудничать друг с другом, чтобы механизм блокировки работал как положено?

Я предполагаю, что каким-то образом CRITICAL_SECTION имеет некоторый статический флаг, чтобы разные его экземпляры могли связываться друг с другом.

Пожалуйста, поправьте меня, если я ошибаюсь.

==== Обновление ====

class MyCriticalSection : protected CRITICAL_SECTION
{
public:
    MyCriticalSection()
    {
        InitializeCriticalSection(this);

    }
    ~MyCriticalSection()
    {       
        DeleteCriticalSection(this);
    }
    void Lock()
    {
        EnterCriticalSection(this);
    }
    void UnLock()
    {
        LeaveCriticalSection(this);
    }
};

class MyClass
{
public:
    MyClass() : m_Int(0) {}

    int GetNum() 
    { 
        m_Lock.Lock();
        // do something here
        m_Lock.UnLock();
        return m_Int; 
    }

    int SetNum(int i)
    {
        m_Lock.Lock();      
        m_Int = i;
        m_Lock.UnLock();
    }
protected:
    int m_Int;
    MyCriticalSection m_Lock;
};

Спасибо

Ответы [ 5 ]

6 голосов
/ 24 марта 2011

Ваше обновление не лучше. Если вы хотите сделать это в стиле RAII, вам нужно работать немного усерднее. Ключ заключается в том, что вам необходимо отделить объект критической секции, который статически размещен, и блокировку, которая является локальной для всех блоков, которые нуждаются в защите.

Следующая фраза взята из Джонатан Доддс , но это классический образец.

class CriticalSection
{
public:
    CriticalSection()
        { ::InitializeCriticalSection(&m_rep); }
    ~CriticalSection()
        { ::DeleteCriticalSection(&m_rep); }

    void Enter()
        { ::EnterCriticalSection(&m_rep); }
    void Leave()
        { ::LeaveCriticalSection(&m_rep); }

private:
    // copy ops are private to prevent copying
    CriticalSection(const CriticalSection&);
    CriticalSection& operator=(const CriticalSection&);

    CRITICAL_SECTION m_rep;
};

Хотя вы могли бы сделать это с наследованием CRITICAL_SECTION, я считаю, что инкапсуляция является более подходящей.

Далее определена блокировка:

class CSLock
{
public:
    CSLock(CriticalSection& a_section)
        : m_section(a_section) { m_section.Enter(); }
    ~CSLock()
        { m_section.Leave(); }

private:
    // copy ops are private to prevent copying
    CSLock(const CSLock&);
    CSLock& operator=(const CSLock&);

    CriticalSection& m_section;
};

Наконец, пример использования:

class Example
{
public:
    bool Process( … );

    …

private:
    CriticalSection m_criticalsection;

    …
};

bool Example::Process( … )
{
    CSLock lock(m_critsection);

    // do some stuff
    …
}

Дело в том, что существует один экземпляр критического раздела, общий для всех потоков. Это то, что заставляет работать критический раздел.

В контрапункте может быть много случаев CSLock, взятых в одном и том же критическом разделе, в одновременном существовании. Это позволяет методу Process() вызываться многими потоками, но его код сериализуется в течение времени жизни экземпляра CSLock, взятого в одном общем критическом разделе.

1 голос
/ 24 марта 2011

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

Фрагмент кода;

class CSLock
{
public:
       CSLock( CriticalSection& cs )
       { 
            EnterCriticalSection( cs );
       }
       ~CSLock()
       {
            LeaveCriticalSection( cs );
       }
 };

 class Client
 {
 public:
       Client()
       {}
       void Fun()
       {
           CSLock( m_cs );
       }
 private:
       CriticalSection m_cs;
 };
1 голос
/ 24 марта 2011

С помощью предложенного вами кода вы блокируете одновременное чтение и одновременную запись, но не блокируете две операции.Чтобы достичь того, чего вы хотите, вы должны использовать один объект CriticalSession.

1 голос
/ 24 марта 2011

Вы совершенно не правы. Два CRITICAL_SECTION абсолютно не связаны - как еще программы могут создавать более одной блокировки? Посмотрите библиотеку boost::thread, чтобы узнать, как проектировать интерфейс.

0 голосов
/ 24 марта 2011

Хм, вы здесь довольно далеко от базы.

Во-первых, хотя у меня нет установок Windows для проверки этого, я почти уверен, что вы не можете наследовать от CRITICAL_SECTION, как это.

Затем, вы не блокируете что-либо с помощью этой настройки, просто замедляя процесс. Каждая запись в Get или Set создает и инициализирует новую критическую секцию, которая специфична для этого потока и данного конкретного вызова. Вам нужно, чтобы CS разделял между вызовами - сделайте их членами класса как минимум.

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