#pragma warning(disable: 4355) // 'this' used before initialized
_thread(ThreadEntry, this)
#pragma warning(default: 4355)
{ }
Метод Kill вызывает виртуальный метод FlushThread — это необходимо для завершения потока из любого состояния ожидания и дает ему возможность запустить _isDying для проверки флажка.
void ActiveObject::Kill() {
_isDying++;
FlushThread();
// Let's make sure it's gone
_thread.WaitForDeath();
}
Мы также имеем каркас для функции ThreadEntry (это — статический метод класса ActiveObject, поэтому мы можем определять соглашение о вызовах, требуемое API). Эта функция выполняется удерживаемым потоком. Параметр, получаемый потоком от системы является тем, который мы передали конструктору объекта потока — это указатель "this" Активного Объекта. API ожидает void-указатель, поэтому мы должны делать явное приведение указателя на ActiveObject. Как только мы овладеваем Активным Объектом, мы вызываем его чистый виртуальный метод InitThread, делать все специфические для реализации приготовления, а затем вызываем основной рабочий метод Run. Реализация метода Run оставлена клиенту каркаса.
DWORD WINAPI ActiveObject::ThreadEntry(void* pArg) {
ActiveObject* pActive = (ActiveObject*)pArg;
pActive->InitThread();
pActive->Run();
return 0;
}
Объект Thread — это тонкая инкапсуляция API. Обратите внимание на флажок CREATE_SUSPENDED, который гарантирует, что нить не начнет выполняться прежде, чем мы не закончим конструирование объекта ActiveObject.
class Thread {
public:
Thread(DWORD(WINAPI* pFun)(void* arg), void* pArg) {
_handle = CreateThread(
0, // Security attributes
0, // Stack size
pFun, pArg, CREATE_SUSPENDED, &_tid);
}
~Thread() {
CloseHandle(_handle);
}
void Resume() {
ResumeThread(_handle);
}
void WaitForDeath() {
WaitForSingleObject(_handle, 2000);
}
private:
HANDLE _handle;
DWORD _tid; // thread id
};
Синхронизация — это то, что действительно делает многозадачный режим столь интенсивно используемым. Давайте, начнем со взаимных исключений. Класс Mutex — тонкая инкапсуляция API. Вы внедряете Mutexes (мутации) в ваш Активный Объект, а затем используете их через Блокировки. Блокировка (Lock) — умный объект, который создается на стеке. В результате чего, во время обслуживания, ваш объект защищен от любых других потоков. Класс Lock — одно из приложений методологии Управления ресурсами. Вы должны поместить Lock внутри всех методов вашего Активного Объекта, которые разделяют доступ к данным с другими потоками.
class Mutex {
friend class Lock;
public:
Mutex() {
InitializeCriticalSection(&_critSection);
}
~Mutex() {
DeleteCriticalSection(&_critSection);
}
private:
void Acquire() {
EnterCriticalSection(&_critSection);
}
void Release() {
LeaveCriticalSection(&_critSection);
}
CRITICAL_SECTION _critSection;
};
class Lock {
public:
// Acquire the state of the semaphore
Lock(Mutex& mutex) : _mutex(mutex) {
_mutex.Acquire();
}
// Release the state of the semaphore
~Lock() {
_mutex.Release();
}
private:
Mutex& _mutex;
};
Событие — это сигнальное устройство, которое потоки используют, чтобы связаться друг с другом. Вы внедряете Событие (Event) в ваш активный объект. Затем Вы переводите удерживаемый поток в состояние ожидания, пока некоторый другой поток не освободит его. Не забудьте однако, что, если ваш удерживаемй поток ожидает события, он не может быть завершен. Именно поэтому Вы должны вызывать Release из метода Flush.
class Event {
public:
Event() {
// start in non-signaled state (red light)
// auto reset after every Wait
_handle = CreateEvent(0, FALSE, FALSE, 0);
}
~Event() {
CloseHandle(_handle);
}