00001 /* 00002 * CntmLib - Подсчет ссылок, потоки, синхронизация, асинхронные процедуры, события 00003 * Copyright (c) 2005, Овсеевич Роман, CntmLib@mail.ru 00004 * _______________________________________________________________________________ 00005 * Разрешено свободное использование, копирование, распространение, изменение 00006 * (изменение сведений об авторских правах запрещено). Запрещена продажа и 00007 * включение всей библиотеки или ее частей в другие библиотеки. В сведениях об 00008 * авторских правах на программу (или сведениях о программе, об авторах, 00009 * использованных средствах разработки и т.д.) должна быть указана информация 00010 * о библиотеке CntmLib, ее авторе и, возможно, сайте или email'е. 00011 * Библиотека поставляется "как есть", без каких-либо гарантий со стороны автора. 00012 */ 00013 00014 #ifndef CNTM_SYNCHROOBJECT_H 00015 #define CNTM_SYNCHROOBJECT_H 00016 #include <Cntm/AsyncProc/Internal/ThreadProcFunctor.h> 00017 #include <Cntm/AsyncProc/Internal/DeferProcFunctor.h> 00018 #include <Cntm/Synchro/IBasicSynchro.h> 00019 #include <Cntm/Synchro/SynchroSpace.h> 00020 #include <Cntm/Synchro/ExtraSynchroSpace.h> 00021 00022 namespace Cntm 00023 { 00024 00025 class Sync; 00026 class ReentrantSync; 00027 00028 /** 00029 * Базовый класс для синхрообъектов. Для того, чтобы создать свой класс синхрообъекта, его нужно унаследовать от классов Cntm::SynchroRefBase, Cntm::SynchroRefBaseEx или Cntm::SynchroAggregatedBase (эти объекты добавляют класс Cntm::SynchroObject как примесь к соответствующей реализации подсчета ссылок). 00030 * 00031 * Синхрообъекты - это объекты со встроенными средствами синхронизации. Синхрообъект обязательно принадлежит определенному синхропространству (см. класс Cntm::SynchroSpace). Синхропространство задается при создании объекта и впоследствии не меняется. 00032 * 00033 * Одна из важнейших функций синхрообъекта в многопоточных приложениях - эксклюзивный вход в синхропространство для выполнения действий, требующих синхронизации (т.е. они не должны быть прерваны другими действиями). Синхропространство в данном случае выступает как критическая секция или мьютекс, единая для всех объектов, принадлежащих синхропространству. Вход и выход из синхропространства выполняются методами Enter(), TryEnter() и Leave() или с помощью классов Cntm::Sync и Cntm::ReentrantSync (рекомендуется использовать классы, т.к. они повышают удобство и надежность). Вызов методов ...Enter() и Leave() аналогичен вызову соответствующих методов синхропространства. Процедуры входа в синхропространство рекурсивны. Принципы синхронизации описаны в классе Cntm::SynchroSpace. 00034 * 00035 * Рекомендации по расположению точек входа в синхропространство. Если в системе используется только главное синхропространство и несколько потоков, то точки входа в синхропространство рекомендуется располагать в потоковых процедурах перед выполнением тех действий и вызовом тех методов, которые требуют синхронизации. Если в системе имеется несколько синхропространств, то вход в синхропространство рекомендуется делать в начале каждого интерфейсного метода (т.е. метода, который м.б. вызван из другого синхропространства), требующего синхронизации. Во внутренних методах (т.е. тех, которые непосредственно не вызываются из других синхропространств) делать точки входа в синхропространство не обязательно, т.к. эти методы вызываются уже из синхропространства. Пример: 00036 * \code 00037 * void Method1(int Arg) 00038 * { 00039 * Sync s(*this); // Точка входа в синхропространство до конца метода. 00040 * ... 00041 * } 00042 * \endcode 00043 * 00044 * Дополнительно синхрообъект предоставляет методы для запуска асинхронных отложенных процедур (метод Defer()()) и асинхронных потоковых процедур (метод Thread()()). Асихронный означает, что поток выполнения не ожидает конца запущенной процедуры, а продолжает работу сразу же после запуска. Отложенная процедура - это подготовленный вызов метода объекта (т.е. с указанием аргументов, которые будут переданы методу при вызове), который будет выполнен, когда будет возможен очередной вход в синхропространство. Асинхронные отложенные процедуры описаны в классе Cntm::AsyncProc. Реально отложенные процедуры реализованы через механизм отложенных синхрозаданий, описанный в документации по классу Cntm::SynchroSpace. Отложенные процедуры могут использоваться для синхронизированного вызова методов из разных потоков выполнения без входа в синхропространство. Асинхронная потоковая процедура - это метод объекта, который выполняется в отдельном потоке. Потоковые процедуры также позволяют передавать любые аргументы в метод, который будет выполняться в отдельном потоке. Асинхронные потоковые процедуры описаны в классе Cntm::AsyncProc. Пример (асинхронный подсчет кол-ва слов в тексте): 00045 * \code 00046 * class Class1: public SynchroRefBase 00047 * { 00048 * int CalcWordsCount(const string& Text, const string& Delims) { ... return res; } 00049 * 00050 * void Main() 00051 * { 00052 * string txt; cin >> txt; 00053 * AsyncProcResult<int> res = ThreadProc(&Class1::CalcWordsCount)(txt, " .,;:!?"); // Создание потока, выполняющего CalcWordsCount и передача ему текста txt и символов-разделителей. 00054 * ... // Выполнение каких либо полезных действий, например запись текста в файл. 00055 * cout << "Кол-во слов = " << res; // Вывод кол-ва слов на экран. 00056 * } 00057 * }; 00058 * \endcode 00059 * 00060 * При использовании синхрообъектов повышается удобство подключения к отложенным событиям (класс Cntm::DeferEvent) и вызов асинхронных отложенных процедур (методы Cntm::AsyncProc::Defer()()). Для работы этих средств требуется указание синхропространства, т.к. для синхрообъектов оно определено изначально, то нет нужды явно его указывать. 00061 * 00062 * Синхронное создание объектов. Вызов конструктора синхрообъекта должен происходить внутри синхропространства, поэтому перед инициализацией объекта следует войти в то пространство, которому будет принадлежать объект. В качестве параметра конструктору передается синхропространство, к которому будет принадлежать объект. Имеется две формы конструкторов SynchroObject: 00063 * - первая форма явно принимает синхропространство. Она имеет вид: SynchroObject::SynchroObject(const SynchroSpace::Ptr& Space = SynchroSpace::Ptr()). Эта форма используется, когда операция создания синхрообъекта уже выполняется внутри пространства, к которому будет принадлежать новый объект. Если синхропространство не указано, то будет использовано главное синхропространство (т.о. если в программе используется только главное синхропространство, то нет необходимости указывать его явно). Если синхропространство явно не указано, а главное синхропространство отсутствует, то генерируется исключение Cntm::IllegalStateException. 00064 * - вторая форма принимает в качестве аргумента ссылку на объект синхросекции (классы Cntm::Sync или Cntm::ReentrantSync). Она имеет вид: SynchroObject::SynchroObject(Sync& ConstructSync) или SynchroObject::SynchroObject(ReentrantSync& ConstructSync). Эта форма используется, когда объект создается в новом или просто другом синхропространстве. В этом случае конструктор синхрообъекта пытается извлечь синхропространство из ConstructSync и если ConstructSync был проинициализирован, то использует извлеченное синхропространство. Если был указан пустой ConstructSync, то первым делом конструктор синхрообъекта создает дополнительное синхропространство (класс Cntm::ExtraSynchroSpace) и присваивает его ConstructSync (который передан по ссылке). Т.о. работа конструктора SynchroObject и конструкторов производных классов всегда будет осуществляться внутри синхпространства данного объекта. 00065 * 00066 * Рекомендации по конструкторам пользовательских классов синхрообъектов. Если в системе используется только главное синхропространство, то никаких параметров использовать не нужно, объект автоматически будет принадлежать главному синхропространству. Если гарантируется, что объект будет создан внутри того синхропространства, к которому он будет принадлежать (обычно это внутренние объекты), то следует использовать только первую форму конструктора с явным указанием синхропространства. Если объект создается из произвольного синхропространства (в т.ч. из нового), то следует использовать вторую форму (возможно выбрав какую-то одну секцию, например, Sync). Если предполагается универсальное использование класса, то следует предоставить обе формы конструкторов (соответственно, три типа конструкторов). Примеры классов: 00067 * \code 00068 * // Класс, который создается в указанном синхропространстве. 00069 * class Class1: public SynchroRefBase 00070 * { 00071 * public: 00072 * typedef RefPtr<Class1> Ptr; 00073 * Class1(const SynchroSpace::Ptr& Space = SynchroSpace::Ptr()): SynchroRefBase(Space) {} 00074 * }; 00075 * 00076 * // Использование. 00077 * ... 00078 * Class1::Ptr c1 = new Class1; // Главное синхропространство. 00079 * ... 00080 * Class1::Ptr c2 = new Class1(this->Space()); // Явно указанное синхропространство, в него должен быть произведен вход перед использованием. 00081 * ... 00082 * 00083 * // Класс, который создается в произвольном синхропространстве. Класс не использует реентерабельный вход, поэтому ограничимся только одной синхросекцией - Cntm::Sync. 00084 * class Class2: public SynchroRefBase 00085 * { 00086 * public: 00087 * typedef RefPtr<Class2> Ptr; 00088 * Class2(Sync& ConstructSync): SynchroRefBase(ConstructSync) {} 00089 * }; 00090 * 00091 * // Использование. 00092 * ... 00093 * Sync s; 00094 * Class2::Ptr c3 = new Class2(s); // Создает дополнительное синхрпространство, производит в него вход (заполняя s). 00095 * // На момент выхода из конструктора s будет содержать новое дополнительное синхропространство. 00096 * ... 00097 * Sync is(parent->Space()); // Войти в синхропространство. 00098 * Class2::Ptr c4 = new Class2(is); // Объект будет создан в синхропространстве parent->Space(). 00099 * ... 00100 * \endcode 00101 * 00102 * Синхронное уничтожение объектов. Подразумевает, что вызов деструкторов для классов Cntm::SynchroRefBase и Cntm::SynchroRefBaseEx и виртуального метода Cntm::SynchroRefBaseEx::OnReleaseInstance() будет производится отложенно синхронно по правилам для отложенных процедур или событий (см. выше) через механизм отложенных синхрозаданий (см. класс Cntm::SynchroSpace). Это значит, что вызов методов деинициализации будет происходить не в тот момент, когда будет утеряна последняя ссылка на объект, а когда можно будет осуществить отложенный синхронный вход в синхропространство, к которому принадлежал объект. 00103 * 00104 * Данный класс обеспечивает многопоточность. 00105 * @author Овсеевич Р. 00106 * \ingroup Synchro 00107 */ 00108 class SynchroObject: virtual public IBasicSynchro 00109 { 00110 public: 00111 00112 typedef RefPtr<SynchroObject> Ptr; 00113 00114 /** 00115 * Возвращает синхропространство, к которому принадлежит синхрообъект. Результат не может принять значение NULL. 00116 */ 00117 SynchroSpace::Ptr Space() const { return space; } 00118 00119 /** 00120 * Возвращает текущий режим входа в синхропространство. Возвращает true, если вход во !все! синхропространства, в которых поток выполнения находится в данный момент, был произведен в реентерабельном режиме. Если был хотя бы один вход в нереентерабельном режиме, то возвращается false. 00121 * 00122 * Метод должен вызываться только после входа в синхропространство и в том же потоке, который произвел вход. 00123 * 00124 * Вызывает Space()->IsReentrantMode(). 00125 */ 00126 bool IsReentrantMode() const { return space->IsReentrantMode(); } 00127 00128 /** 00129 * Возвращает текущий режим входа в синхропространство. Возвращает true, если вход во !все! синхропространства, в которых поток выполнения находится в данный момент, был произведен в реентерабельном режиме. Если был хотя бы один вход в нереентерабельном режиме, то возвращается false. 00130 * 00131 * Метод должен вызываться только после входа в синхропространство и в том же потоке, который произвел вход. 00132 * 00133 * Вызывает Space()->Enter(). 00134 * @param ReentrantMode - режим входа (реентерабельный или нет). Описание режимов входа приведено в описании класса Cntm::SynchroSpace. 00135 */ 00136 void Enter(bool ReentrantMode = false) { space->Enter(ReentrantMode); } 00137 00138 /** 00139 * Возвращает текущий режим входа в синхропространство. Возвращает true, если вход во !все! синхропространства, в которых поток выполнения находится в данный момент, был произведен в реентерабельном режиме. Если был хотя бы один вход в нереентерабельном режиме, то возвращается false. 00140 * 00141 * Метод должен вызываться только после входа в синхропространство и в том же потоке, который произвел вход. 00142 * 00143 * Вызывает Space()->TryEnter(). 00144 * @param ReentrantMode - режим входа (реентерабельный или нет). Описание режимов входа приведено в описании класса Cntm::SynchroSpace. 00145 */ 00146 bool TryEnter(bool ReentrantMode = false) { return space->TryEnter(ReentrantMode); } 00147 00148 /** 00149 * Производит выход из синхропространства. Парный метод для Enter() и TryEnter() (когда последний вернул true). Параметр ReentrantMode должен иметь такое же значение, как и у парного ему метода Enter() и TryEnter(). 00150 * 00151 * Более подробное описание режимов входа и случаев, когда вход в синхропространство выполняется из другого синхропространства приведенов в описании класса Cntm::SynchroSpace. 00152 * 00153 * Вызывает Space()->Leave(). 00154 * @param ReentrantMode - режим входа (реентерабельный или нет). Описание режимов входа приведено в описании класса Cntm::SynchroSpace. 00155 */ 00156 void Leave(bool ReentrantMode = false) { space->Leave(ReentrantMode); } 00157 00158 /** 00159 * Запустить асинхронную потоковую процедуру. Первым параметром указывается адрес метода, 00160 * который будет выполнен в новом потоке. Описание остальных параметров, возвращаемого 00161 * значения и принципов работы приведено в описании класса Cntm::AsyncProc и метода 00162 * Cntm::AsyncProc::Thread()(). После вызова метода Thread() в скобках указываются 00163 * передаваемые потоковой функции аргументы. Thread(...) может рассматриваться как 00164 * идентификатор функции, а вторая пара скобок - как ее вызов. 00165 * 00166 * Пример: 00167 * \code 00168 * Thread(&Class1::Thread1)(Arg1, Arg2); 00169 * Thread(&Class1::Thread2)(); 00170 * \endcode 00171 */ 00172 template < typename MethodSignatureT > 00173 SpecUtils::ThreadProcFunctor < typename SignatureInfo < MethodSignatureT > ::FuncSign > Thread( 00174 MethodSignatureT Method, 00175 BasicAsyncProc::ThreadPriority Priority = BasicAsyncProc::tpNormal, 00176 bool HoldRef = true) 00177 { 00178 return SpecUtils::ThreadProcFunctor<typename SignatureInfo<MethodSignatureT>::FuncSign>( 00179 GenericMethodNoRefPtr<typename SignatureInfo<MethodSignatureT>::FuncSign>( 00180 static_cast<typename SignatureInfo<MethodSignatureT>::ClassType*>(this), Method), Priority, HoldRef, false); 00181 } 00182 00183 /** 00184 * Запустить асинхронную отложеную процедуру. Первым параметром указывается адрес метода, 00185 * который будет выполнен в синхронно отложенно с использованием механизма отложенных 00186 * синхрозаданий (см. класс Cntm::SynchroSpace). Описание остальных параметров, 00187 * возвращаемого значения и принципов работы приведено в описании класса Cntm::AsyncProc 00188 * и метода Cntm::AsyncProc::Defer()(). После вызова метода Defer() в скобках указываются 00189 * передаваемые отложенной функции аргументы. Defer(...) может рассматриваться как 00190 * идентификатор функции, а вторая пара скобок - как ее вызов. 00191 * 00192 * Пример: 00193 * \code 00194 * Defer(&Class1::ShowMsg)(Arg1, Arg2); 00195 * Defer(&Class1::Start)(); 00196 * \endcode 00197 */ 00198 template < typename MethodSignatureT > 00199 SpecUtils::DeferProcFunctor < typename SignatureInfo < MethodSignatureT > ::FuncSign > Defer( 00200 MethodSignatureT Method, 00201 bool HoldRef = true) 00202 { 00203 return SpecUtils::DeferProcFunctor<typename SignatureInfo<MethodSignatureT>::FuncSign>( 00204 GenericMethodNoRefPtr<typename SignatureInfo<MethodSignatureT>::FuncSign>( 00205 static_cast<typename SignatureInfo<MethodSignatureT>::ClassType*>(this), Method), Space(), HoldRef, false); 00206 } 00207 00208 /** 00209 * Создать отсроченную асинхронную потоковую процедуру. Первым параметром указывается 00210 * адрес метода, который будет выполнен в новом потоке. Описание остальных параметров, 00211 * возвращаемого значения и принципов работы приведено в описании класса Cntm::AsyncProc 00212 * и метода Cntm::AsyncProc::SuspendThread()(). После вызова метода SuspendThread() в 00213 * скобках указываются передаваемые потоковой функции аргументы. SuspendThread(...) может 00214 * рассматриваться как идентификатор функции, а вторая пара скобок - как ее вызов. 00215 * 00216 * Пример: 00217 * \code 00218 * SuspendThread(&Class1::Thread1)(Arg1, Arg2); 00219 * SuspendThread(&Class1::Thread2)(); 00220 * \endcode 00221 */ 00222 template < typename MethodSignatureT > 00223 SpecUtils::ThreadProcFunctor < typename SignatureInfo < MethodSignatureT > ::FuncSign > SuspendThread( 00224 MethodSignatureT Method, 00225 BasicAsyncProc::ThreadPriority Priority = BasicAsyncProc::tpNormal, 00226 bool HoldRef = true) 00227 { 00228 return SpecUtils::ThreadProcFunctor<typename SignatureInfo<MethodSignatureT>::FuncSign>( 00229 GenericMethodNoRefPtr<typename SignatureInfo<MethodSignatureT>::FuncSign>( 00230 static_cast<typename SignatureInfo<MethodSignatureT>::ClassType*>(this), Method), Priority, HoldRef, true); 00231 } 00232 00233 /** 00234 * Создать отсроченную асинхронную отложеную процедуру. Первым параметром указывается 00235 * адрес метода, который будет выполнен в синхронно отложенно с использованием механизма 00236 * отложенных синхрозаданий (см. класс Cntm::SynchroSpace). Описание остальных параметров, 00237 * возвращаемого значения и принципов работы приведено в описании класса Cntm::AsyncProc 00238 * и метода Cntm::AsyncProc::SuspendDefer()(). После вызова метода SuspendDefer() в скобках 00239 * указываются передаваемые отложенной функции аргументы. SuspendDefer(...) может 00240 * рассматриваться как идентификатор функции, а вторая пара скобок - как ее вызов. 00241 * 00242 * Пример: 00243 * \code 00244 * SuspendDefer(&Class1::ShowMsg)(Arg1, Arg2); 00245 * SuspendDefer(&Class1::Start)(); 00246 * \endcode 00247 */ 00248 template < typename MethodSignatureT > 00249 SpecUtils::DeferProcFunctor < typename SignatureInfo < MethodSignatureT > ::FuncSign > SuspendDefer( 00250 MethodSignatureT Method, 00251 bool HoldRef = true) 00252 { 00253 return SpecUtils::DeferProcFunctor<typename SignatureInfo<MethodSignatureT>::FuncSign>( 00254 GenericMethodNoRefPtr<typename SignatureInfo<MethodSignatureT>::FuncSign>( 00255 static_cast<typename SignatureInfo<MethodSignatureT>::ClassType*>(this), Method), Space(), HoldRef, true); 00256 } 00257 00258 protected: 00259 00260 /** 00261 * Конструктор используется, когда операция создания синхрообъекта уже выполняется внутри пространства, к которому будет принадлежать новый объект. Если синхропространство не указано, то будет использовано главное синхропространство (т.о. если в программе используется только главное синхропространство, то нет необходимости указывать его явно). 00262 * 00263 * @param Space - (опционально) используется для задания синхропространства, к которому будет принадлежать создаваемый объект. 00264 * 00265 * Исключение Cntm::IllegalStateException - если синхропространство явно не указано, а главное синхропространство отсутствует 00266 */ 00267 SynchroObject(const SynchroSpace::Ptr& Space = Cntm::SynchroSpace::Ptr()); 00268 00269 /** 00270 * Конструктор используется, когда объект создается в новом или просто другом синхропространстве. В этом случае конструктор синхрообъекта пытается извлечь синхропространство из ConstructSync и если ConstructSync был проинициализирован, то использует извлеченное синхропространство. Если был указан пустой ConstructSync, то первым делом конструктор синхрообъекта создает дополнительное синхропространство (класс Cntm::ExtraSynchroSpace) и присваивает его ConstructSync (который передан по ссылке). Т.о. работа конструктора SynchroObject и конструкторов производных классов всегда будет осуществляться внутри синхпространства данного объекта. 00271 * 00272 * @param ConstructSync - ссылка на синхросекцию, которая обеспечивает нахождение в синхропространстве при создании объекта. 00273 */ 00274 SynchroObject(Sync& ConstructSync); 00275 00276 /** 00277 * Конструктор используется, когда объект создается в новом или просто другом синхропространстве. В этом случае конструктор синхрообъекта пытается извлечь синхропространство из ConstructSync и если ConstructSync был проинициализирован, то использует извлеченное синхропространство. Если был указан пустой ConstructSync, то первым делом конструктор синхрообъекта создает дополнительное синхропространство (класс Cntm::ExtraSynchroSpace) и присваивает его ConstructSync (который передан по ссылке). Т.о. работа конструктора SynchroObject и конструкторов производных классов всегда будет осуществляться внутри синхпространства данного объекта. 00278 * 00279 * @param ConstructSync - ссылка на реентерабельную синхросекцию, которая обеспечивает нахождение в синхропространстве при создании объекта. 00280 */ 00281 SynchroObject(ReentrantSync& ConstructSync); 00282 00283 /** 00284 * Деструктор. 00285 */ 00286 virtual ~SynchroObject() {} 00287 00288 private: 00289 00290 friend class SynchroRefBase; 00291 friend class SynchroRefBaseEx; 00292 friend class SynchroDeleteTask; 00293 00294 /** 00295 * Класс синхрозадачи, которая выполняет операции деинициализации объекта. Ее выполнение состоит из вызова метода SynchroDeleteInstance удаляемого объекта. 00296 * 00297 * Эта задача не реентерабельна. 00298 * @author Овсеевич Р. 00299 */ 00300 class SynchroDeleteTask: public SynchroSpace::TaskBase 00301 { 00302 public: 00303 00304 /** 00305 * Конструктор. 00306 */ 00307 SynchroDeleteTask(SynchroObject* DeletedObject): deletedObject(DeletedObject) {} 00308 00309 /** 00310 * Выполнить задачу. Вызывает метод SynchroDeleteInstance() удаляемого объекта. 00311 */ 00312 void Exec(bool Recursive) { deletedObject->SynchroDeleteInstance(); } 00313 00314 /** 00315 * Задача не рекурсивная. 00316 */ 00317 bool IsRecursive() const { return false; } 00318 00319 private: 00320 00321 /** 00322 * Обычный указатель на удаляемую задачу. 00323 */ 00324 SynchroObject* deletedObject; 00325 }; 00326 00327 00328 /** 00329 * Ссылочный указатель на синхропространство, к которому принадлежит синхрообъект. Не может принять значение NULL. 00330 */ 00331 SynchroSpace::Ptr space; 00332 00333 /** 00334 * Виртуальный метод, вызываемый синхронно отложенно, после того, как объект перешел в удаляемое состояние. Реализации вызывают метод DeleteInstance() классов Cntm::RefBase и Cntm::RefBaseEx. 00335 */ 00336 virtual void SynchroDeleteInstance() {} 00337 00338 /** 00339 * Запустить синхронный отложенный вызов метода SynchroDeleteInstance() через механиз отложенных синхрозаданий. Вызывается в момент перехода в удаляемое состояние из методов DeleteInstance() реализаций. 00340 */ 00341 void CallSynchroDeleteInstance() { space->AddSynchroTask(new SynchroDeleteTask(this)); } 00342 }; 00343 00344 } 00345 00346 #endif //CNTM_SYNCHROOBJECT_H
© Овсеевич Р.В. Документация по CntmLib 1.1.4 от 28 May 2008. Создано системой 1.5.3 |