|
|
|
| |
Главная С++ (часть 3) Функция isRunningO
Далее приведен КОД функции isRunning (). // Возвращает true, если сборщик выполняется. static bool isRunning() { return instCount > 0; } Она просто сравнивает значение instcount с 0. Пока переменная instcount больше 0, по крайней мере, один указатель типа GCPtr существует и, следовательно, все еще необходим сбор мусора. Синхронизация доступа к списку gclist Многие функции класса GCPtr обращаются к переменной gclist, которая содержит информационный список сбора мусора. Доступ к ней необходимо синхронизировать для того, чтобы исключить попытки двух или нескольких потоков одновременно использовать gclist. Причину этого легко понять. Если доступ не синхронизировать, то, например, один поток может полупить итератор, указывающий на конец списка, и в это же время другой поток Добавит или удалит элемент списка. В этом случае итератор станет неверным. Во избежание подобных проблем любой фрагмент кода, обращающийся к списку gclist, должен быть защищен мьютексом. Копирующий конструктор, код которого приведен далее, — один из примеров синхронизации доступа. // Копирующий конструктор. GCPtr(const GCPtr &ob) { if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc(); list<GCInfo<T> >::iterator p; p = findPtrlnfo(ob.addr); p->refcount++; // increment ref count addr = ob.addr; arraySize = ob.arraySize; if(arraySize > 0) isArray = true; else isArray = false; instCount++; // увеличивает счетчик экземпляров с учетом копии ReleaseMutex(hMutex); } Обратите внимание на то, что копирующий конструктор начинает с запроса мьютекса. Затем он создает копию объекта и увеличивает счетчик ссылок для фрагмента памяти, на которую указывает созданный объект. На выходе копирующий конструктор освобождает мьютекс. Этот базовый метод применяется во всех функциях, обращающихся к списку gclist. Два дополнительных изменения Есть еще два изменения, которые мы должны сделать в первоначальной версии сборщика мусора. Во-первых, напоминаю, что в первоначальной версии объявлена статическая переменная first для индикации момента создания первого объекта GCPtr. Теперь эта переменная не нужна, ее роль исполнит переменная hMutex. Следовательно, удалите из класса GCPtr переменную first. Поскольку это статическая переменная, вы должны также удалить ее определение, размещенное вне тела класса GCPtr. В первоначальной однопоточной версии сборщика мусора вы могли наблюдать за сбором мусора, если определили макрос DISPLAY. Большая часть его кода удалена в многопоточной версии, так как многопоточность делает вывод программы беспорядочным и в большинстве случаев непонятным-В многопоточной версии определение макроса DISPLAY позволяет вам про-сто узнать, когда запускается сборщик мусора и когда он завершает работу. ролная версия многопоточного сборщика мусора В листинге 3.3 приведена полностью многопоточная версия сборщика мусора. Назовите этот файл gcthrd.h. jjj^MHr З.з. Сборщик мусора, выполняющийся как фоновая задача #include <iostream> #include <list> #include <typeinfo> #include <cstdlib> #include <windows.h> #include <process.h> using namespace std; // Для наблюдения за сбором мусора включите DISPLAY. // #define DISPLAY // Генерируется исключение при попытке // использовать итератор Iter, выходящий за границы // базового объекта (underlying object). // class OutOfRangeExc { // Добавьте обработку, необходимую вашему приложению. }; // Генерируется исключение при превышении времени ожидания // исключительного права доступа к hMutex. // class TimeOutExc { // Добавьте обработку, необходимую вашему приложению. }; Ч Класс, подобный итератору, для обработки в цикле массивов, Ч на которые указывают объекты GCPtr. Указатели Iter Ч ** не ** участвуют в сборе мусора и не влияют на Ч него. Таким образом, указание с помощью Iter на какой-либо Ч объект не препятствует удалению этого объекта из памяти. // template <class T> class Iter { T *ptr; // текущее значение указателя T *end; ' // указатель на элемент, следующий за последним Т *begin; // указывает на первый элемент размещенного массива unsigned length; // длина последовательности (массива) public: Iter() { ptr = end = begin = NULL; length = 0; } Iter(T *p. T *first, T *last) { ptr = p; end = last; begin - first; length = last - first; } // Возвращает длину последовательности, на которую // этот указатель Iter ссылается, unsigned size() return length; } // Возвращает значение, на которое указывает ptr. //Не допускает обращения за границы. Т &operator*() { if( (ptr >= end) || (ptr < begin) ) throw OutOfRangeExc(); return *ptr; } // Возвращает адрес, содержащийся в ptr. //Не допускает обращения за границы. Т *operator->() { if( (ptr >= end) || (ptr < begin) ) throw OutOfRangeExc(); return ptr; } // Префикс ++. Iter operator++() { ptr++; return *this; } // Префикс —. Iter operator—() { ptr—; return *this; } // Постфикс ++. Iter operator++(int notused) { T *tmp = ptr; ptr++; return Iter<T>(tmp, begin, end); } // Постфикс —. Iter operator—(int notused) { T *tmp = ptr; ptr—; return Iter<T>(titp, begin, end); } // Возвращает ссыпку на объект с заданным индексом. //Не допускает обращения за границы. Т &operator[](int i) { if( (i < 0) || (i >= (end-begin)) ) throw OutOfRangeExc(); return ptr[i]; } / / Определяет операции отношения. bool operator==(Iter op2) { return ptr == op2.ptr; > bool operator!=(Iter op2) { return ptr != op2.ptr; } bool operator<(Iter op2) { return ptr < op2.ptr; } bool operator<=(Iter op2) { return per <= op2.ptr; } bool operator>(Iter op2) { return ptr > op2.ptr; } bool operator>=(Iter op2) { return ptr >= op2.ptr; } // Вычитает целое значение из Iter. Iter operator-(int n) ( ptr -= n; return *this; } // Прибавляет целое значение к Iter. Iter operator+(int n) { ptr += n; return *this; } // Возвращает количество элементов между двумя итераторами Iter, int operator-(Iter<T> &itr2) { return ptr - itr2.ptr; } }; // Этот класс описывает элемент, который хранится 11 в информационном списке сбора мусора.' // teDplate <class Т> class GClnfo { public: unsigned refcount; // текущий счетчик ссылок Т *memPtr; // указатель на выделенную память /* isArray равен true если memPtr указывает на размещенный массив. isArray равен false в противном случае. */ bool isArray; // true, если указывает на массив /* Если memPtr указывает на размещенный массив, то arraySize содержит размер массива */ unsigned arraySize; // размер массива // Здесь mPtr указывает на выделенную память. // Если это массив, то size задает // размер массива. GClnfo(Т *mPtr, unsigned size=0) { refcount = 1; memPtr = mPtr; if(size != 0) isArray = true; else isArray. = false; arraySize = size; } }; Ч Перегрузка операции — позволяет сравнивать объекты GClnfo. 11 Это необходимо для класса list из библиотеки STL. template <class Т> bool operator== (const GCInfo<T> &obl, const GCInfo<T> &ob2) { return (obi.memPtr == ob2.memPtr); } II Класс GCPtr реализует тип указателя, который применяется при сборе // мусора для очистки неиспользуемой памяти. // Класс GCPtr должен применяться для указания на память, // которая выделена динамически с помощью операции new. // Если используется для ссылки на размещенный массив, // в параметре size задается размер массива. // template <class Т, int size=0> class GCPtr { // gclist содержит список сбора мусора, static list<GCInfo<T> > gclist; // addr указывает на выделенную память,на которую // этот указатель GCPtr в данный момент ссылается. Т *addr; /* isArray равен true если этот указатель GCPtr указывает на размещенный массив. isArray равен false в противном случае. */ bool isArray; // true, если указывает на массив // Если этот указатель GCPtr ссылается на размещенный // массив, arraySize содержит размер массива, unsigned arraySize; // размер массива // Переменные для поддержки многопоточности. unsigned tid; // идентификатор потока static HANDLE hThrd; // дескриптор потока static HANDLE hMutex; // дескриптор мьютекса static int instCount; // счетчик объектов GCPtr // Возвращает итератор для указателя на GClnfo в списке gclist. typename list<GCInfo<T> >::iterator findPtrInfo(T *ptr); public: // Определяет тип iterator для GCPtr<T>. typedef Iter<T> GCiterator; // Создает как инициализированные, так и неинициализированные объекты. GCPtr(Т *t=NULL) { // Когда создается первый объект, создает также мыотекс //и регистрирует функцию shutdown(). if(hMutex==0) { hMutex = CreateMutex(NULL, 0, NULL); atexit(shutdown); } if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc(); list<GCInfo<T> >::iterator p; p = findPtrlnfo(t) ,- // Если элемент есть в списке gciist, // увеличивает его счетчик ссылок на единицу. //В противном случае добавляет этот элемент в список. if(p != gciist.end()) p->refcount++; // наращивает счетчик ссылок else { // Создает и запоминает этот элемент. GCInfo<T> gcObj(t, size); gciist,push_front(gcObj); } addr = t; arraySize = size; if(size > 0) isArray = true; else isArray = false; // Наращивает счетчик экземпляров при создании // каждого нового объекта. instCount++; // Если поток для сбора мусора // не выполняется, запускает его. if(hThrd==0) { hThrd = (HANDLE) _beginthreadex(NULL, 0, gc, (void *) 0, 0, (unsigned *) &tid) ; // Для некоторых приложений лучше понизить приоритет // сборщика мусора, как показано далее: // // SetThreadPriority(hThrd, // THREAD_PRIORITY_BELOW_NORMAL); } ReleaseMutex(hMutex); } // Копирующий конструктор. GCPtr(const GCPtr &ob) { if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc(); list<GCInfo<T> >::iterator p; p = findPtrlnfo(ob.addr); p->refcount++; // увеличиваем счетчик ссылок addr = ob.addr; arraySize = ob.arraySize; if(arraySize > 0) isArray = true; else isArray = false; instCount++; // увеличиваем счетчик экземпляров с учетом копии ReleaseMutex(hMutex); > // Деструктор объекта GCPTr. -GCPtrO ; // Собирает мусор. Возвращает true, если хотя бы // один объект уничтожен, static bool collectO; // Перегружает присваивание указателя объекту GCPtr. rj *operator=(T *t); // Перегружает присваивание одного объекта GCPtr // другому объекту GCPtr. GCPtr &operator=(GCPtr &rv); // Возвращает ссылку на объект, на который указывает // этот экземпляр GCPtr. Т &operator*() { return *addr; } // Возвращает адрес, на который указывает объект GCPtr. Т *operator->() {return addr; } // Возвращает ссылку на объект с индексом, заданным i. Т &operator[](int i) { return addr[i]; } // Функция преобразовании для T *. operator Т *() { return addr; } // Возвращает Iter на начало выделенного фрагмента памяти. Iter<T> begin() { int size; if(isArray) size = arraySize; else size = 1; return Iter<T>(addr, addr, addr + size); } // Возвращает Iter на элемент, следующий за последним //в размещенном массиве. Iter<T> end() { int size; if(isArray) size = arraySize; else size = 1; return Iter<T>(addr + size, addr, addr + size); } // Возвращает размер списка gclist для этого типа // объектов GCPtr. static int gclistSizeO { if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc(); unsigned sz = gclist.size(); ReleaseMutex(hMutex); return sz; } // Функция-утилита для вывода на экран списка gclist. static void showlistO; // Следующие функции обеспечивают многопоточность. // // Возвращает true, если сборщик выполняется, static bool isRunning() { return instcount > 0; } // Очищает список gclist, когда программа завершается, static void shutdown(); // Точка входа для потока сборщика мусора, static unsigned _stdcall gc(void * param); }; // Отводит память для статических переменных, template <class Т, int size> list<GCInfo<T> > GCPtr<T, size>::gclist; template <class T, int size> int GCPtr<T, size>::instCount = 0; tenplate <class T, int size> HANDLE GCPtr<T, size>::hMutex = 0; tewplaCe <class T, int size> HANDLE GCPtr<T, size>::hThrd = 0; // Деструктор для объекта GCPtr. template <class T, int size> . GCPtr<T, size>: :-GCPtrO { if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc () ; list<GCInfo<T> >::iterator p; p = findPtrInfo(addr); if(p->refcount) p->refcount—; // операция декремента для // счетчика ссылок // Уменьшает на единицу счетчик экземпляров // при каждом уничтожении объекта. Instcount—; ReleaseMutex(hMutex) ; } // Собирает мусор. Возвращает true, если хотя бы // один объект удален, template <class Т, int size> bool GCPtr<T, size>::collect() { if(WaitForSingleObj ect(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc () ; bool memfreed = false; list<GCInfo<T> >::iterator p; do { // Просматривает список gclist для поиска // неопределенных указателей. for(p = gclist.begin(); p != gclist.end(); p++) { // Если элемент используется, пропускает, if(p->refcount > 0) continue; memfreed = true; // Удаляет неиспользуемые элементы из списка gclist. gclist.remove(*p); // Освобождает память, если Free указатель GCPtr равен null, if(p->memPtr) { if(p->isArray) { delete!] p->memPtr; // удаляет массив } else { delete p->memPtr; // удаляет одиночный элемент } } // Возобновляет поиск, break; } . } while(р != gclist.end()); ReleaseMutex(hMutex); return memfreed; } // Перегружает присваивание указателя объекту GCPtr. template <class T, int size> T * GCPtr<T, size>::operators(T *t) { if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc () ; list<GCInfo<T> >::iterator p; // Сначала уменьшает на единицу счетчик ссылок // для фрагмента памяти, на который ссылается в данный момент. р = findPtrlnfo(addr); p->refcount—; // Далее, если новый адрес уже существует в системе, // увеличивает на единицу его счетчик. // Иначе создает новый элемент в списке gciist. р = findPtrlnfo(t); if(p != gciist. end О ) p->refcount++; else { // Создает и запоминает этот элемент. GCInfo<T> gcObj(t, size); gciist.push_front(gcObj); } addr = t; // score the address. ReleaseMutex(hMutex); return t; } // Перегружает присваивание одного объекта GCPtr другому объекту GCPtr. template <class T, int size> GCPtr<T. size> & GCPtr<T, size>::operator=(GCPtr &rv) { if (WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc(); list<GCInfo<T> >::iterator p; // Сначала уменьшает на единицу счетчик ссылок // для фрагмента памяти, на который ссылается в данный момент. Р = findPtrinfo(addr); P->ref count—; // Далее увеличивает на единицу счетчик ссылок // нового объекта. . Р = findPtrInfо(rv.addr); P->refcount+-t-; // наращивает refcount addr = rv.addr;// запоминает адрес. ReleaseMutex(hMutex); return rv; } // Функция-утилита, отображающая список gclist. template <class T, int size> void GCPtr<T, size2>: rshowlist () { if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) throw TimeOutExc(); list<GCInfo<T> >::iterator p; cout « "gclist<" « typeid(T) .nameO « " « size « ">:\n"; cout « "memPtr refcount value\n"; if(gclist.begin() == gclist.end()) { cout « " — Empty —\n\n"; return; } for(p = gclist.begin(); p != gclist.end(); p++) { cout « " [" « (void *) p->memPtr « " ]" « " " « p->ref count « " " ; if (p->memPtr) cout « " " « *p->memPtr; else cout « " ---"; cout « endl; } cout « endl; ReleaseMutex(hMutex)f } // Находит указатель в списке gclist. template <class T, int size> typename list<GCInfo<T> >::iterator GCPtr<T, size>::findPtrlnfо(T *ptr) { list<GCInfo<T> >::iterator p; // Находит ptr в списке gciist. for(p = gclist.beginO ; p != gciist.end(); p++) if(p->memPtr == ptr) return p; return p; } // Точка входа для потока сборщика мусора, tenplate <class Т, int size> unsigned _stdcall GCPtr<T, size>: :gc(void * param) { #ifdef DISPLAY cout « "Garbage collection started.\n"; #endif while(isRunning()) { collect(); } collect(); // собирает мусор перед выходом из функции // Освобождает и устанавливает дескриптор потока таким образом, // чтобы поток сбора мусора можно было // возобновить при необходимости. CloseHandle (hThrd) ; hThrd = 0; #ifdef DISPLAY cout « "Garbage collection terminated for " « typeid(T) .name() « "\n"; #endif return 0; } ^1 Очищает список gciist, когда программа завершается. template <class T, int size> void GCPtr<T, size>::shutdown() { if (gclistSizeO == 0) return; // список пуст list<GCTnfo<T> >::iterator p; #ifdef DISPLAY cout « "Before collecting for shutdown() for " « typeid(T).name() « "\n"; #endif for(p = gelist.begin(); p != gclist.end(); p++) { // Делает все оставшиеся счетчики ссылок нулевыми. p->refcount = 0; } collect(); #ifdef DISPLAY cout « "After collecting for shutdown() for " « typeid(T).name() « "\n"; #endif }
|
красноярск доставка цветы
|
|
|
|