Вячеслав Любченко
Постановка задачи. Пусть мы имеем двух спортсменов-бегунов и тренера, фиксирующего в динамике число шагов, которые они пробежали на тот или иной момент времени. В целях простоты бегуны стартуют одновременно, бегут с одинаковой скоростью и длина шага у них одна. Необходимо создать программу, моделирующую поведение бегунов и их тренера.
Модель решения. Можно условиться, что спортсмен имеет счетчик шагов, который на каждом шаге увеличивается на единицу. Достигнув заданного числа шагов, "бегуны" останавливаются. "Тренер" в процессе бега отслеживает текущее значения счетчиков каждого спортсмена, выводя их значения на экран монитора (варианты - фиксирует в блокноте, записывает в текстовый файл на диск:). На структурном уровне программа, реализующая поставленную задачу, содержит три параллельных не взаимодействующих между собой объекта (двух бегунов и одного тренера).
Программная реализация. Выделим для каждого объекта свой поток. Код класса модели спортсмена-бегуна может быть таким (Листинг 1): #include "living_object.h" class CDat; class Trotter : public living_object { public: Trotter(CDat *d, int num, long lSteps = 100000000); virtual ~Trotter(); void run(); private: CDat *pCDat; int nNumTrotter; }; Trotter::Trotter(CDat *d, int num, long lSteps): living_object() { pCDat = d; pCDat->nStepsAll = lSteps; nNumTrotter = num; } Trotter::~Trotter() { } void Trotter::run() { for (int i=1; inStepsAll; i++) { if (nNumTrotter ==0) { pCDat->nCurStep0++; } else { pCDat->nCurStep1++; } if (nNumTrotter ==0) { if (pCDat->nStepsAll == pCDat->nCurStep0) break; } else { if (pCDat->nStepsAll == pCDat->nCurStep1) break; } // Sleep(0); } } Листинг 1. Код класса "бегуна".
Здесь класс living_object - базовый класс для любого, так называемого, активного/живого объекта на базе одного потока. Его код представляет Листинг 2. Здесь метод run() реализует жизненный цикл активного объекта. В рамках его каждый объект-спортсмен в зависимости от своего номера - nNumTrotter наращивает в цикле значение соответствующего счетчика шагов.
Первый объект изменяет счетчик nCurStep0, второй - nCurStep1. Когда значение счетчика шагов достигнет заданного числа - nStepsAll, объект завершает свою работу.
class living_object { static DWORD WINAPI HelperThreadProc(LPVOID p); public: living_object(); virtual void run() {}; virtual ~living_object() {}; };
extern HANDLE hStart; extern CRITICAL_SECTION cs; extern int total_threads; extern DWORD endTick;
living_object::living_object() { DWORD threadID; if(!CreateThread(NULL, 0, HelperThreadProc, (LPVOID)this, 0, &threadID)) printf("GetLastError: %d\n", GetLastError()); }
DWORD WINAPI living_object::HelperThreadProc(LPVOID p) { EnterCriticalSection(&cs); total_threads++; LeaveCriticalSection(&cs);
WaitForSingleObject(hStart, INFINITE);
((living_object*)p)->run();
EnterCriticalSection(&cs); total_threads--; endTick=GetTickCount(); LeaveCriticalSection(&cs); return 0; } Листинг 2. Код класса базового активного объекта living_object .
Тренер, бегущий рядом с бегунами, имеет следующий код (Листинг 3):
#include "living_object.h"
class CDat; class PrnCurStep : public living_object { public: PrnCurStep(CDat *d); virtual ~PrnCurStep(); void run(); private: CDat *pCDat; long lTmp0; long lTmp1; };
PrnCurStep::PrnCurStep(CDat *d): living_object() { pCDat = d; lTmp0= lTmp1= 0; }
PrnCurStep::~PrnCurStep() { }
void PrnCurStep::run() { while (pCDat->nStepsAll != pCDat->nCurStep0) { // printf("%d %d\n", pCDat->nCurStep0, pCDat->nCurStep1); ///* if (lTmp0 != pCDat->nCurStep0 lTmp1 != pCDat->nCurStep1) { printf("%d %d\n", pCDat->nCurStep0, pCDat->nCurStep1); lTmp0 = pCDat->nCurStep0; lTmp1 = pCDat->nCurStep1; } // Sleep(0); //*/ } }
Листинг 3. Код класса "тренер".
В рамках метода run "тренер" на каждой итерации цикла выводит на консоль текущие значения счетчика циклов обоих бегунов. Тестирование. Запустив программу, моделирующую бегунов, мы получим результат, который показан на рис.1.






