На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела FAQ в группе разделов С++.
1. Раздел FAQ предназначен для публикации готовых статей.
2. Здесь нельзя задавать вопросы, для этого существуют соответствующие разделы:
Чистый С++
Visual C++ / MFC / WTL / WinApi
Borland C++ Builder
COM / DCOM / ActiveX / ATL
Сопутствующие вопросы
3. Внимание, все темы и сообщения в разделе премодерируются. Любое сообщение или тема будут видны остальным участникам только после одобрения модератора.
Модераторы: B.V., Qraizer
  
> Windows API Tips & tricks , #1 (Critical sections)
    Наверняка у каждого в арсенале есть набор мелких полезных классиков для работы с Win API, которые, может, не представляют особой ценности с точки зрения непосредственно предмета программирования, как такового, но сильно облегчают жизнь. Предлагаю делиться опытом!

    Выкладывание своих Tipsов и tricksов начну с достаточно простого, но полезного способа организации работы с критическими секциями (Critical sections) - незаменимого средства организации доступа к данным в многопоточном приложении.

    Для работы с критическими секциями в Win32 API основными являются 4 метода:

    InitializeCriticalSection
    DeleteCriticalSection
    EnterCriticalSection
    LeaveCriticalSection

    Рассмотрим пример использования этих в функций для разграничения доступа к объекту CMyDataStorage. Предполагается использование объекта этого класса несколькими потоками (запись/чтение данных). Для простоты примера ограничимся одним методом класса, вызов которого требуется защитить критической секцией.

    В первом приближении наш класс (опустим часть его членов и кода, реализующую непосредственно хранение данных) будет выглядеть так:
    ExpandedWrap disabled
      class CMyDataStorage
      {
        public:
          CMyDataStorage ();
          ~CMyDataStorage ();
       
          // Требуется защитить этот метод от одновременного вызова из нескольких потоков
          void PutData (void *pData, DWORD dwSize);
       
        protected:
          // Виндовый объект - критическая секция
          CRITICAL_SECTION m_cs;
      };
       
      CMyDataStorage::CMyDataStorage ()
      {
        // Инициализируем крит. секцию
        ::InitializeCriticalSection (&m_cs);
      }
       
      CMyDataStorage::~CMyDataStorage ()
      {
        // Крит. секция нам больше не нужна
        ::DeleteCriticalSection (&m_cs);
      }
       
      void CMyDataStorage::PutData (void *pData, DWORD dwSize)
      {
        /*
        Вход в критическую секцию вызовет остановку работы потока до тех пор, пока другой поток,
        уже вошедший в эту крит. секцию не выйдет из нее, вызвав LeaveCriticalSection
        */
        ::EnterCriticalSection (&m_cs);
       
        /*  { Block 1
        Здесь предполагается сохранение данных во внутренних сруктурах объекта класса
        } Block 1 */  
       
        /*
        Обязательно вызвать LeaveCriticalSection симметрично каждому вызову EnterCriticalSection
        */
        ::LeaveCriticalSection (&m_cs);
      }

    Что плохо в приведенном коде? Внешне все выглядит хорошо. Но посмотрим на опущенный участок кода (внутри Block 1). Предположим, что данные, содержащиеся в pData, сложны по своей структуре, и требуется их "раскрутить" перед тем, как положить на хранение в объект класса.

    При этом возможен многократный вызов return внутри этого блока, что обязывает перед каждым таким return вызывать LeaveCriticalSection. Код становится более громоздким. А вдруг (от ошибок никто не застрахован), случится исключение. Тогда LeaveCriticalSection так и не будет вызвано, и потоки, ожидающие своей очереди в EnterCriticalSection, так и останутся там навечно.

    Для упрощения жизни предлагается примитивный классик, который тем не менее, позволит нам не думать обо всем перечисленном.
    ExpandedWrap disabled
      class zLock  
      {
      public:
        zLock(LPCRITICAL_SECTION pCS)
          : m_pCS (pCS)  
        {
          // при конструировании класса вызываем EnterCriticalSection
          ::EnterCriticalSection(m_pCS);
        }
          
        ~zLock()
        {
          // в деструкторе делаем LeaveCriticalSection
          ::LeaveCriticalSection(m_pCS);
        }
      protected:
        LPCRITICAL_SECTION m_pCS;
      };

    Тогда приведенный выше метод PutData сводится к такому виду:
    ExpandedWrap disabled
      void CMyDataStorage::PutData (void *pData, DWORD dwSize)
      {
        // коструируем статический объект класса zLock
        zLock Lock(&m_cs);
       
        /*  { Block 1
        Здесь предполагается сохранение данных во внутренних сруктурах объекта класса
        } Block 1 */
       
        /*
        вызовом LeaveCriticalSection теперь занимается объект Lock
        */
      }

    На входе в метод сконструируется объект класса zLock, который обеспечит вызов EnterCriticalSection. Этот же объект нам обеспечит вызов LeaveCriticalSection - при любом возврате из метода, как по return, так и по возникновению исключения, деструктор для Lock будет вызван автоматически. Соответственно, автоматом сработает LeaveCriticalSection. И наступит черед другого потока выполнять данный метод.

    По-моему достаточно элегантное решение. Можно еще более облегчить свою жизнь (это уже на любителя - особого смысла в этом нет)
    ExpandedWrap disabled
      class zCriticalSection
      {
        public:
          zCriticalSection ()
            { ::InitializeCriticalSection (&m_cs); }
       
          ~zCriticalSection ()
            { ::DeleteCriticalSection (&m_cs); }
       
          operator const LPCRITICAL_SECTION ()
            { return &m_cs; }
        protected:
          CRITICAL_SECTION m_cs;
      };

    Тогда преобразуем исходный класс в окончательный вид:
    ExpandedWrap disabled
      class CMyDataStorage
      {
        public:
          CMyDataStorage ();
          ~CMyDataStorage ();
       
          void PutData (void *pData, DWORD dwSize);
       
        protected:
          // Объект - хелпер к виндовой критической секции
          zCriticalSection m_cs;
      };
       
      CMyDataStorage::CMyDataStorage ()
      {
      }
       
      CMyDataStorage::~CMyDataStorage ()
      {
      }
       
      void CMyDataStorage::PutData (void *pData, DWORD dwSize)
      {
        zLock Lock(m_cs);
       
        /*  { Block 1
        Здесь предполагается сохранение данных во внутренних сруктурах объекта класса
        } Block 1 */
      }


    Думаю, что многие из вас сталкивались с разработкой многопточных приложений и уже делали подобные trickи, но этот маленький хелп в первую очередь ориентирован на тех, кто только осваивает эту интересную область программирования под Windows.

    Как всегда, отзывы, пожелания - Welcome!

    Keep on coding B) Peace!
      Если в Block 1 произойдёт эксепшн, то деструктор zLock не будет вызван и следов. критическая секция не будет освобожденна. Это маленькая бага, и не всегда она может проявится....но иногда очень гнусная :)
        AlexSm, я так понимаю, это зависит от того, какие исключения использовать.. Если стандартные С++ ные, то все нормально пройдет.
        Тока что в статье AQL'а прочитал :D. (да и так знал ;))
          Ваще имхо для таких целей лучше использовать блоки
          ExpandedWrap disabled
             
            __try
            {
            ...
            __leave; // Вместо return;
            ...
            }
            __finaly
            {
            // Тут всё что нужно закрываем
            }
            Цитата

            Тока что в статье AQL'а прочитал . (да и так знал )


            В упомянутой статье сказано, почему так лушче никогда не делать.
              А можно я тоже поделюсь? Я мучался-мучался с портированием программ, работающих с com-портом на разных ОС и сделал простенький класс, который лежит здесь: http://el.h10.ru/sources.php
              Вроде бы все работает хорошо, может кому пригодится.
                EL[michlen], создай для этого отдельную тему в этом разделе. И вставь в нее свою статью.
                1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                0 пользователей:


                Рейтинг@Mail.ru
                [ Script execution time: 0,0387 ]   [ 14 queries used ]   [ Generated: 21.05.24, 13:28 GMT ]