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_ACTIVEOBJECT_H 00015 #define CNTM_ACTIVEOBJECT_H 00016 #include <Cntm/SystemUtils/AtomicUtils.h> 00017 #include <Cntm/Containers/Register.h> 00018 #include <Cntm/RefCount/IRefObject.h> 00019 #include <Cntm/RefCount/RefPtr.h> 00020 #include <Cntm/Synchro/SyncScopes/Sync.h> 00021 #include <Cntm/Synchro/SyncScopes/ReentrantSync.h> 00022 #include <Cntm/ActiveObjects/Internal/BasicActiveThreadImpl.h> 00023 #include <Cntm/ActiveObjects/ActiveThreadTerminatingSignal.h> 00024 #include <Cntm/ActiveObjects/ActiveThread.h> 00025 00026 namespace Cntm 00027 { 00028 00029 /** 00030 * Активный объект - это объект, имеющий свои собственные потоки выполнения. Для того, чтобы 00031 * создать свой класс активного объекта, его нужно унаследовать от классов Cntm::ActiveRefBase, 00032 * Cntm::ActiveRefBaseEx, Cntm::ActiveSynchroRefBase или Cntm::ActiveSynchroRefBaseEx (эти 00033 * объекты добавляют класс Cntm::ActiveObject как примесь к соответствующей реализации 00034 * подсчета ссылок или реализации синхронизации). 00035 * 00036 * Средства, предоставляемые базовыми классами активных объектов - управление потоками 00037 * активных объектов (класс Cntm::ActiveThread). Активные объекты могут создавать свои потоки 00038 * с помощью метода Cntm::ActiveObject::StartThread()() или Cntm::ActiveThread::Start()(). В 00039 * качестве потока выступает какой-либо метод объекта, которому при запуске можно передать 00040 * любые аргументы. Потоки активных объектов являются единицами выполнения (класс 00041 * Cntm::ActiveThread унаследован от Cntm::ExecutionUnit) и взаимодействие с потоками 00042 * производится через хэндлы потоков (класс Cntm::ActiveThreadHandle), которые возвращаются 00043 * как результат запуска. Как любая единица выполнения, поток активного объекта м.б. 00044 * принудительно остановлен, для этого через хэндл ему посылается сигнал останова (процедуры 00045 * останова подробно описаны в классе Cntm::ExecutionUnit). 00046 * 00047 * Пример: 00048 * \code 00049 * class Class1: public ActiveSynchroRefBase 00050 * { 00051 * ActiveThreadHandle hdl; 00052 * 00053 * void Thread1(int a, string s) { ... } 00054 * 00055 * void Start() 00056 * { 00057 * StartThread(&Class1::Thread1)(45, "Text"); 00058 * hdl = ActiveThread::Start(this, &Class1::Thread1)(45, "Text"); 00059 * } 00060 * }; 00061 * \endcode 00062 * Активные объекты являются ссылочными объектами. Важной особенностью является то, что при 00063 * создании потока активного объекта кол-во ссылок на объект не увеличивается. В этом состоит 00064 * отличие потоков в активных объектах от асинхроных потоковых процедур AsyncProc::Thread()(), 00065 * т.к. при выполнении асинхронных процедур держится ссылка на объект, поэтому, пока 00066 * выполняется процедура, объект не может быть уничтожен. Второй важной функцией, которую 00067 * предоставляет Cntm::ActiveObject, является автоматический останов потоков этого объекта 00068 * перед уничтожением объекта. Когда на объект не осталось ссылок и он переходит в удаляемое 00069 * состояние, всем потокам этого объекта автоматически посылается сигнал останова. 00070 * Гарантируется, что деструктор (а для классов от Cntm::RefBaseEx и метод OnReleaseInstance()) 00071 * будут вызваны только после завершения всех потоков (или иначе - все потоки (которые 00072 * являются методами активного объекта) завершатся до вызова деструктора). Т.о. если нужен 00073 * такой поток выполнения, который бы не блокировал уничтожение ссылочного объекта, а 00074 * завершался, когда на объект не осталось ссылок, то для этого можно применять активные 00075 * объекты и их потоки. 00076 * 00077 * Выше говорилось, что поток активного объекта не создает новую ссылку на объект, а также, 00078 * что поток завершаетмя после перехода объекта в удаляемое состояние (т.е. может 00079 * выполняться, когда поток находится как в рабочем, так и в удаляемомм состоянии). Если 00080 * потоку в процессе выполнения требуется ссылочный указатель на свой объект, то он его 00081 * может получить из обычного, например, из this. При этом следует помнить, что если объект 00082 * находится в удаляемом состоянии, то преобразование из обычного в ссылочный будет приводить 00083 * к тому, что ссылочный указатель примет значение NULL. 00084 * 00085 * Для того, чтобы объект при отсутствии на него ссылок смог уничтожиться, все его потоки 00086 * должны остановиться. Для обнаружения сигнала останова в потоках можно использовать 00087 * стандартные средства, предлагаемые классом Cntm::ActiveThread - 00088 * Cntm::ActiveThread::Terminated() и Cntm::ActiveThread::CheckTerminating(). Первый просто 00089 * возвращает значение флага останова потока, второй проверяет, и если он установлен, то 00090 * генерирует специальное исключение Cntm::ActiveThreadTerminatingSignal. 00091 * 00092 * Использование Using-объектов. Using-объекты (класс Cntm::Using, пакет Concurrency) - это 00093 * scoped-объекты, наподобии auto_ptr и т.п., являющиеся по сути ссылочными указателями на 00094 * активные объекты. Using-объекты преследуют 2 цели: 1 - при создании они проверяют флаг 00095 * завершения потока и переданный ссылочный указатель на NULL (т.е. объект уже находится в 00096 * удаляемом состоянии), если какое либо из этих условий истинно, то генерируется специальное 00097 * исключение Cntm::ActiveThreadTerminatingSignal. 2 цель - хранить полученный ссылочный 00098 * указатель на всем протяжении времени существования Using-объекта, т.о. гарантируется, что 00099 * при выполнении кода, находящегося между созданием и деинициализацией Using-объекта 00100 * активный объект не перейдет в удаляемое состояние. Для синхрообъектов имеются специальные 00101 * Using-объекты, объединенные с синхросекциями - классы Cntm::UsingSync и 00102 * Cntm::UsingReentrantSync, которые, кроме всего прочего, производят вход в 00103 * синхропространство, к которому принадлежит активный синхрообъект. Пример метода 00104 * активного объекта, выполняющегося в потоке: 00105 * \code 00106 * void Thread1(int a, string s) 00107 * { 00108 * while (true) // Бесконечный цикл, в котором выполяниются повторяющиеся операции. 00109 * { 00110 * sleep(1); 00111 * ... 00112 * // Вход в Using-секцию. Либо вход будет успешный, либо, если поток 00113 * // необходимо завершить, то будет сгенерировано исключение 00114 * // ActiveThreadTerminatingSignal и произойдет завершение потока, т.к. 00115 * // оно не перехватывается внутри цикла. 00116 * Using use(*this); 00117 * 00118 * ... // В этом блоке объект всегда находится не в удаляемом состоянии и 00119 * // преобразование обычного указателя (например, this) к ссылочному 00120 * // никогда не приведет к результату NULL. 00121 * 00122 * // Выход из блока. 00123 * use.Unuse(); 00124 * ... 00125 * // Код, специфичный для синхрообъектов. 00126 * // Вход в Using-секцию, в дополнение производится вход в синхропространство. 00127 * UsingSync suse(*this); 00128 * ... // Находимся в синхропространстве. Деинициализация произойдет в деструкторе suse. 00129 * } 00130 * } 00131 * \endcode 00132 * В заключение приведем диаграмму, отражающую время жизни потока по отношению к времени жизни объекта: 00133 * \code 00134 * Жизнь объекта - Жизнь потока 00135 * ----------------- Начало создания объекта. 00136 * | _ 00137 * | |<-Запуск потока. 00138 * | | 00139 * ----------------- Выход из метода запуска объекта, работа. 00140 * | | 00141 * | | 00142 * | |<-Выполнение потока, когда объект находится в рабочем состоянии (на него есть ссылки). 00143 * | | 00144 * | | 00145 * ----------------- Переход объекта в удаляемое состояние (на него не осталось ссылок), потоку сообщается о завершении. 00146 * | | 00147 * | |<-Выполнение потока, когда объект находится в удаляемом состоянии. 00148 * | | Поток может узнать о завершении статическим методом ActiveThread::Terminated() или конструкцией Using. 00149 * | | После того как поток узнает о завершении он должен выполнить процедуру завершения. 00150 * | |<-Завершающие действия потока, объект находится в удаляемом состоянии. 00151 * | X 00152 * | 00153 * ----------------- Вызов деструктора или метода OnReleaseInstance когда запущенных потоков не осталось (для синхрообъектов - 00154 * | когда будет возможен вход в синхропространство). 00155 * | 00156 * | Выполнение деинициализации объекта. 00157 * X 00158 * \endcode 00159 * 00160 * Замечание по созданию потоков в конструкторе. Создавать потоки в конструкторе активного 00161 * объекта нельзя, т.к. при создании потока временно создается ссылочный указатель на данный 00162 * объект. Почему нельзя использовать ссылочные указатели на объект в его конструкторе 00163 * рассмотрено в разделе "Подсчет ссылок". 00164 * 00165 * Данный класс обеспечивает многопоточность. 00166 * @author Овсеевич Р. 00167 * \ingroup ActiveObjects 00168 */ 00169 class ActiveObject: virtual public IRefObject 00170 { 00171 public: 00172 00173 typedef RefPtr<ActiveObject> Ptr; 00174 00175 /** 00176 * Запустить поток для данного активного объекта. 00177 * 00178 * После вызова метода StartThread() в скобках указываются аргументы, которые следует 00179 * передать запускаемому в новом потоке методу. Если аргументов нет, ставятся круглые 00180 * скобки. Если после вызова метода StartThread() не будет второй пары скобок, то поток 00181 * запущен не будет. Пример: 00182 * \code 00183 * StartThread(&Class1::Thread1)(45, "Text"); 00184 * StartThread(&Class1::Thread2)(); 00185 * \endcode 00186 * Метод StartThread(...) олицетворяет идентификатор функции, а вторая пара скобок - его 00187 * вызов, поэтому когда говорится о методе StartThread()() и подобных ему, то указывается 00188 * две пары скобок. 00189 * 00190 * Метод StartThread()() возвращает хэндл созданного потока (см. класс Cntm::ActiveThreadHandle). 00191 * 00192 * Исключение: SystemException - системная ошибка при создании нового потока. 00193 * @param Method - указатель на метод активного объекта, который будет вызван в новом потоке. 00194 * @param Priority - приоритет создаваемого потока. 00195 */ 00196 template < typename MethodSignatureT > 00197 SpecUtils::ActiveThreadFunctor < typename SignatureInfo < MethodSignatureT > ::FuncSign > StartThread( 00198 MethodSignatureT Method, 00199 ActiveThread::ThreadPriority Priority = ActiveThread::tpNormal) 00200 { 00201 return ActiveThread::Start( 00202 static_cast<typename SignatureInfo<MethodSignatureT>::ClassType*>(this), Method, Priority); 00203 } 00204 00205 protected: 00206 00207 typedef Register<SpecUtils::BasicActiveThreadImpl::Ptr> ThreadsRegister; 00208 00209 /** 00210 * Конструктор по умолчанию 00211 */ 00212 ActiveObject(): threadsCount(1) {} 00213 00214 /** 00215 * Деструктор. 00216 */ 00217 virtual ~ActiveObject() {} 00218 00219 /** 00220 * Служебный виртуальный метод. Вызывается, когда все потоки завершились. В потомках 00221 * вызывает методы DeleteInstance() классов, реализующих подсчет ссылок. 00222 */ 00223 virtual void AllThreadsFinished() = 0; 00224 00225 /** 00226 * Сообщить всем потокам об останове и перейти в режим ожидания завершения всех потоков 00227 * объекта. 00228 * 00229 * Служебный метод. Когда все потоки будут завершены, вызовется AllThreadsFinished() для 00230 * дальнейшей деинициализации. 00231 * 00232 * Данный метод вызывается при переходе объекта в удаляемое состояние из метода 00233 * DeleteInstance(). 00234 * 00235 * В промежутке от вызова TerminateAllThreads() до AllThreadsFinished() (состояние 00236 * ожидания завершения потоков) объект находится в удаляемом состоянии, поэтому 00237 * создание новых потоков невозможно. 00238 */ 00239 void TerminateAllThreads(); 00240 00241 private: 00242 00243 friend class SpecUtils::BasicActiveThreadImpl; 00244 00245 /** 00246 * Возвращает перечень, в котором регистрируются потоки. 00247 */ 00248 ThreadsRegister& GetThreadsRegister() { return threadsRegister; } 00249 00250 /** 00251 * Вызывается перед запуском потока. Увеличивает счетчик кол-ва потоков. 00252 */ 00253 void ThreadStarted() { threadsCount.Inc(); } 00254 00255 /** 00256 * Вызывает CheckAllThreadsFinished() для уменьшения счетчика кол-ва потоков и его проверки. 00257 */ 00258 void ThreadFinished() { CheckAllThreadsFinished(); } 00259 00260 private: 00261 00262 /** 00263 * Перечень потоков. При создании поток заносит туда свой собственный ссылочный указатель, 00264 * а когда завершается, то изымает его из перечня. 00265 */ 00266 ThreadsRegister threadsRegister; 00267 00268 /** 00269 * Кол-во потоков + 1. Атомарно прибавляется и отнимается 1 при создании и уничтожении 00270 * потоков. Дополнительно отнимается 1 в TerminateAllThreads(). Начальное значение 1. 00271 */ 00272 SpecUtils::AtomicVariable threadsCount; 00273 00274 /** 00275 * Уменьшает атомарный счетчик потоков. Когда счетчик достигает 0, вызывает 00276 * AllThreadsFinished(). Данный метод вызывается, когда завершается поток или объект 00277 * переходит в удаляемое состояние. Перед вызовом AllThreadsFinished() счетчик потоков 00278 * перводится в исходное значение - 1. 00279 */ 00280 void CheckAllThreadsFinished(); 00281 }; 00282 00283 } 00284 00285 #endif //CNTM_ACTIVEOBJECT_H
© Овсеевич Р.В. Документация по CntmLib 1.1.4 от 28 May 2008. Создано системой 1.5.3 |