ActiveObject.h

См. документацию.
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

SourceForge.net Logo
© Овсеевич Р.В. Документация по CntmLib 1.1.4 от 28 May 2008. Создано системой  doxygen 1.5.3