Сообщений 7 Оценка 407 Оценить |
Исходный код (класс GuardedT ) - 2 Кб
Примеры использования ("многопоточные" cout и vector) - 4 Кб
STL и многие другие библиотеки написаны с учётом эффективности и удобства, но без учёта многопоточности процессов ( и это правильно ), но как же использовать эту кучу кода в многопоточных приложениях без написания эквивалентной кучи кода? Чтобы решить эту проблему, я написал простой класс (заголовочный файл "mthread.h")
GuardedT<"класс, доступ к объекту которого должен быть синхронизирован", "класс блокировки"> |
позволяющий использовать немногопоточные типы в многопоточной среде:
/////////////////////////////////////////////////////////////////////////////////////////////// template <class T, class G> class GuardedT { public: typedef T value_type; // что защищаем typedef G guard_type; // чем защищаем typedef void (*destroy_type)( T*, G* ); // тип функции очистки // вы можете описать свою функцию ( не член ) и передать её конструктору // наиболее часто встречающиеся ( у меня :) варианты static void deleteAll( T* t, G* g ) { delete t; delete g; } static void deleteOnlyGuard( T*, G* g ) { delete g; } static void deleteOnlyValue( T* t, G* ) { delete t; } static void deleteNothing( T*, G* ) { } static void deleteArrayValueAndGuard( T* t, G* g ) { delete[] t; delete g; } // указатели позволяют вызывать конструкторы с любыми параметрами // GuardedT<deque<int>,CritSect> gints( new deque<int>(...), new CritSect(...) ); GuardedT( T* t, G* g, destroy_type destr = GuardedT<T,G>::deleteAll ) : mT(*t), mG(*g), mDestroy(destr) { } // просто очищаем с помощью переданной функции очистки ~GuardedT() { mDestroy( &mT, &mG ); } // эти функции позволяют заблокировать доступ к защищаемому типу // пока, скажем, мы пробегаем по нему итератором ( если это какой-нибудь контейнер ) void lock() { mG.lock(); } void unlock() { mG.unlock(); } // этот класс позволяет блокировать только одну операцию над защищаемым типом // например вывод в cout или push_back для vector // блокировка разблокируется даже если операция вызовет исключение class GL { public: ~GL() { mG.unlock(); } T* operator->() { return &mT; } T& operator*() { return mT; } // этот нужен чтобы можно было вызывать operator .. (...) // через предыдущий такие операторы не вызвать ( разве тока ->operator[](...) - бред ) private: T& mT; G& mG; // запрещаем копирование GL& operator=( const GL& ); friend class GuardedT<T,G>; GL( T& t, G& g ) : mT(t), mG(g) { mG.lock(); } }; // class GuardedT<T,G>::GL // две следующих функции блокируют доступ на время вызова какой-либо функции-члена защищаемого объекта GL operator->() { return GL( mT, mG ); } GL operator*() { return GL( mT, mG ); } // а эти можно использовать для незащищённого доступа // например, до их вызова мы заблокировались с помощью lock() T& value() { return mT; } G& guard() { return mG; } private: T& mT; G& mG; destroy_type mDestroy; // запрещаем копирование GuardedT( const GuardedT& gt ); GuardedT& operator=( const GuardedT& ); }; // class GuardedT<T,G> /////////////////////////////////////////////////////////////////////////////////////////////// |
Также в этом заголовочном файле находятся классы Mutex, CritSect, Semaphore и Event. Они написаны мной для использования в одном приложении, поэтому защиту ( SECURITY_ATTRIBUTES ) и доступ к этим объектам из других процессов я не реализовывал. Вы можете написать любые блокировочные типы и использовать их с GuardedT<...>, которому важно лишь существование функций-членов lock() и unlock(). А можно написать класс с пустыми lock() и unlock()... ну ладно, извращайтесь сами.
Примеры использования смотри в файлах
test.cpp - показывает, как вообще работать с этими классами,
test_ostream.cpp - показывает, что происходит при несинхронизированном выводе через cout и
test_vector.cpp - показывает, что происходит при несинхронизированном доступе к vector<int>
батники ( *.bat ) - для компиляции.
Вы имеете право на адвоката... тьфу ты, вы можете использовать и переделывать этот код (под другие типы исключений, под другие стандарты именования, например, всё начинается с заглавной буквы, и т.п.) неограниченно, но указывайте у кого взяли код (да-да блокировочные классы не очень, но здесь я ставлю на GuardedT<...>) (я ни у кого такого хорошего кода ( да и похуже также ) не видел, а идея синхронизации возникла вместе с идеей о многопоточности).
НИ НА ЧЬЁ АВТОРСТВО Я НЕ ПОКУШАЮСЬ.
ИДЕЮ И КЛАСС GuardedT<...> Я ПРИДУМАЛ И НАПИСАЛ САМ,
ВОЗМОЖНО, ЧТО ПОДОБНОЕ УЖЕ КТО-ТО ПРИДУМАЛ И НАПИСАЛ ДО МЕНЯ,
И КТО-ТО ПРИДУМАЕТ И НАПИШЕТ ПОЗЖЕ.
НУ ЧТО Ж, ИНОГДА У ЛЮДЕЙ МОГУТ ВОЗНИКНУТЬ ОДНИ И ТЕ ЖЕ МЫСЛИ
КАК И, ТИПА, "ПОРА ПОЖРАТЬ" И В СТОЛОВОЙ ОЧЕРЕДЬ ДО ДРУГОЙ СТОЛОВОЙ
И ВСЁ ТАКОЕ :)
ЮЗАЙТЕ НА ЗДОРОВЬЕ
Сообщений 7 Оценка 407 Оценить |