Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.137.164.210] |
|
Сообщ.
#1
,
|
|
|
Наверняка у каждого в арсенале есть набор мелких полезных классиков для работы с Win API, которые, может, не представляют особой ценности с точки зрения непосредственно предмета программирования, как такового, но сильно облегчают жизнь. Предлагаю делиться опытом!
Выкладывание своих Tipsов и tricksов начну с достаточно простого, но полезного способа организации работы с критическими секциями (Critical sections) - незаменимого средства организации доступа к данным в многопоточном приложении. Для работы с критическими секциями в Win32 API основными являются 4 метода: InitializeCriticalSection DeleteCriticalSection EnterCriticalSection LeaveCriticalSection Рассмотрим пример использования этих в функций для разграничения доступа к объекту CMyDataStorage. Предполагается использование объекта этого класса несколькими потоками (запись/чтение данных). Для простоты примера ограничимся одним методом класса, вызов которого требуется защитить критической секцией. В первом приближении наш класс (опустим часть его членов и кода, реализующую непосредственно хранение данных) будет выглядеть так: 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, так и останутся там навечно. Для упрощения жизни предлагается примитивный классик, который тем не менее, позволит нам не думать обо всем перечисленном. 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 сводится к такому виду: void CMyDataStorage::PutData (void *pData, DWORD dwSize) { // коструируем статический объект класса zLock zLock Lock(&m_cs); /* { Block 1 Здесь предполагается сохранение данных во внутренних сруктурах объекта класса } Block 1 */ /* вызовом LeaveCriticalSection теперь занимается объект Lock */ } На входе в метод сконструируется объект класса zLock, который обеспечит вызов EnterCriticalSection. Этот же объект нам обеспечит вызов LeaveCriticalSection - при любом возврате из метода, как по return, так и по возникновению исключения, деструктор для Lock будет вызван автоматически. Соответственно, автоматом сработает LeaveCriticalSection. И наступит черед другого потока выполнять данный метод. По-моему достаточно элегантное решение. Можно еще более облегчить свою жизнь (это уже на любителя - особого смысла в этом нет) class zCriticalSection { public: zCriticalSection () { ::InitializeCriticalSection (&m_cs); } ~zCriticalSection () { ::DeleteCriticalSection (&m_cs); } operator const LPCRITICAL_SECTION () { return &m_cs; } protected: CRITICAL_SECTION m_cs; }; Тогда преобразуем исходный класс в окончательный вид: 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! |
Сообщ.
#2
,
|
|
|
Если в Block 1 произойдёт эксепшн, то деструктор zLock не будет вызван и следов. критическая секция не будет освобожденна. Это маленькая бага, и не всегда она может проявится....но иногда очень гнусная
|
Сообщ.
#3
,
|
|
|
AlexSm, я так понимаю, это зависит от того, какие исключения использовать.. Если стандартные С++ ные, то все нормально пройдет.
Тока что в статье AQL'а прочитал . (да и так знал ) |
Сообщ.
#4
,
|
|
|
Ваще имхо для таких целей лучше использовать блоки
__try { ... __leave; // Вместо return; ... } __finaly { // Тут всё что нужно закрываем } |
Сообщ.
#5
,
|
|
|
Цитата Тока что в статье AQL'а прочитал . (да и так знал ) В упомянутой статье сказано, почему так лушче никогда не делать. |
Сообщ.
#6
,
|
|
|
А можно я тоже поделюсь? Я мучался-мучался с портированием программ, работающих с com-портом на разных ОС и сделал простенький класс, который лежит здесь: http://el.h10.ru/sources.php
Вроде бы все работает хорошо, может кому пригодится. |
Сообщ.
#7
,
|
|
|
EL[michlen], создай для этого отдельную тему в этом разделе. И вставь в нее свою статью.
|