Контрольные работы по курсу "Программное обеспечение систем управления"
Реализация синхронизации двух потоков с использованием Семафора
Пример реализации алгоритма на языке Visual C++ 6.0.
Автор: Лантвойт О. В.
Кафедра КСУ, МГТУ "СТАНКИН"
Опубликовано: 19.01.2005
Версия текста: 1.0
Скачать исходные коды примера для VC++ 6.0
Постановка задачи.
Необходимо реализовать синхронизацию двух потоков, которые инициализируют массив целых чисел. Этот массив будет разделяемым ресурсом. В качестве средства синхронизации нужно использовать симафор.
Решение поставленной задачи.
Для демонстрации работы семафора используем в качестве потоков, зависящие от времени потоки. Пусть это будет гонка двух потоков, скорость выполнения которых будет зависеть от задержки, генерируемой случайным образом. Для удобства демонстрации примем предел одной задержки равный 1 секунде, а количество задержек равным 10. В качестве разделяемого ресурса используем массив двадцати целых чисел.
Графическое решение задачи.
Реализация на С++. Листинг программы.
Поток №1.
static DWORD WINAPI FirstThread(void *pv)
{COORD pos; // Переменная, которая будет хранить текущую координату курсора
pos.X=3; // Столбец отображения задержки первого потока
BOOL fDone=FALSE; // Флаг отработки потока
while (!fDone)
{
::WaitForSingleObject(&g_hSemaphore, INFINITE); // Ожидание запуска семафора на длительном интервале времени
if(g_nIndex>=MaxCount) // Проверяю, не создано ли потоков больше чем максимальное кол-во потоков
fDone=TRUE;
else
{
g_nIndex++; // Количество потоков увеличиваю на 1
for (int i=0; i<=10; i++) // Начинаю цикл для инициализации массива 10 временных задержек
{srand(time(NULL)); // Включаю генератор случайных чисел
IndexResours++;
a[IndexResours-1]= rand()%1000; // Генерирую задержку первого потока (от 0 до 1000 миллисекунд)
pos.Y = i;
SetConsoleCursorPosition (hStdout ,pos); // Устанавливаю курсор в i - тую строку
printf("%d", a[IndexResours-1]); // Вывожу текущую задержку для первого потока
Sleep(a[IndexResours-1]); // Осуществляю задерку на текущее количество миллисекунд
}
}
::ReleaseSemaphore(g_hSemaphore,1,0); // Освобождаю семафор от данного потока
}
if (!Lid) // Если лидирующий поток не определён, то делаю данный лидирующим
Lid = 1; return 0;}
Поток №2
static DWORD WINAPI SecondThread(void *pv)
{ COORD pos; // Переменная, которая будет хранить текущую координату курсора
int Speed2[10]; // Массив задержек второго потока
pos.X=25; // Столбец отображения задержки второго потока
BOOL fDone=FALSE; // Флаг отработки потока
while (!fDone)
{
::WaitForSingleObject(&g_hSemaphore, INFINITE); // Ожидание запуска семафора на длительном интервале времени
if(g_nIndex>=MaxCount) // Проверяю, не создано ли потоков больше чем максимальное кол-во потоков
fDone=TRUE;
else
{
g_nIndex++; // Количество потоков увеличиваю на 1
for (int i=0; i<=10; i++) // Начинаю цикл для инициализации массива 10-ти временных задержек
{
IndexResours++;
a[IndexResours-1]= rand()%1000; // Генерирую задержку второго потока (от 0 до 1000 миллисекунд)
pos.Y = i;
SetConsoleCursorPosition (hStdout ,pos); // Устанавливаю курсор в i - тую строку
printf("%d", a[IndexResours-1]); // Вывожу текущую задержку для второго потока
Sleep(a[IndexResours-1]); // Осуществляю задерку на текущее количество миллисекунд
}
}
::ReleaseSemaphore(g_hSemaphore,1,0); // Освобождаю семафор от данного потока
}
if (!Lid) // Если лидирующий поток не определён, то делаю данный лидирующим
Lid = 2; return 0;
}
Из листингов потоков видно, что скорость их выполнения зависит от времени. Далее представлено основное тело программы, в котором организована синхронизация потоков с использованием Семафора.
Основной текст программы
// Semaphore.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include "Semaphore.h"
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;
#endif/////////////////////////////////////////////////////////////////////////////// The one and only application objectCWinApp theApp;
using namespace std;static DWORD WINAPI FirstThread(void *pv); // Прототипы функций
static DWORD WINAPI SecondThread(void *pv); // которые будут участвовать в гонке <designtimesp=21150> потоков
HANDLE g_hSemaphore; // Обьявляем Хендл семафора
HANDLE hStdout; // Хендл вывода информации в консоли
int Lid=0, g_nIndex=0; // Флаг, определяющий поток завершённый первым(лидерство), количество синхронизируемых потоков
int a[20], IndexResours=0; // Общий ресурс и его индекс
LONG MaxCount = 2; // Максимальное количество синхронизируемых семафором потоков
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){ int nRetCode = 0; hStdout = GetStdHandle(STD_OUTPUT_HANDLE); // initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
DWORD dw; // Обьект синхронизации
LONG g_iCount = 0; // начальное значение семафора
HANDLE hThreads[2]; // Массив Хендлов потоков
g_hSemaphore = ::CreateSemaphore(NULL, g_iCount, MaxCount, NULL); // Создаем семафор и инициализируем его Хендл
hThreads[0] = ::CreateThread(NULL, 0, FirstThread, NULL, 0 , &dw); // Создаем потоки
hThreads[1] = ::CreateThread(NULL, 0, SecondThread, NULL, 0 , &dw); // и инициализируем Массив Хендлов потоков
::ResumeThread(hThreads[0]); //Запускаем потоки
::ResumeThread(hThreads[1]);
::WaitForMultipleObjects(2, hThreads, TRUE, INFINITE); // Ожидаем окончание работы 2 потоков на очень длительном интервале времени
::CloseHandle(hThreads[0]); // Закрываем созданные потоки
::CloseHandle(hThreads[1]);
::CloseHandle(g_hSemaphore); // и сам семафор
switch (Lid) //определяем поток, который завершился первым { case 1 : cout << "n nepBbIu' noTok *I*uHuLLIupoBaJI nepBbIM!!!n"; break;
case 2 : cout << "n BTopou' noTok *I*uHuLLIupoBaJI nepBbIM!!!n";
break;
default : break;
} // и выводим информацию о номере этого потока }
cout << endl << "The END!!!" << endl << "Press Any Key..."; // Выводим информацию о завершении работы программы
getch(); // ожидание нажатия клавиши для окончания работы
return nRetCode;
}
Результат возможной работы программы