Контрольные работы по курсу "Программное обеспечение систем управления"

Реализация синхронизации двух потоков с использованием Семафора
Пример реализации алгоритма на языке Visual C++ 6.0.

Автор: Лантвойт О. В.
Кафедра КСУ, МГТУ "СТАНКИН"

Опубликовано: 19.01.2005
Версия текста: 1.0

Скачать исходные коды примера для VC++ 6.0 размер файла: 16.4 Kб

Постановка задачи.

Необходимо реализовать синхронизацию двух потоков, которые инициализируют массив целых чисел. Этот массив будет разделяемым ресурсом. В качестве средства синхронизации нужно использовать симафор.

Решение поставленной задачи.

Для демонстрации работы семафора используем в качестве потоков, зависящие от времени потоки. Пусть это будет гонка двух потоков, скорость выполнения которых будет зависеть от задержки, генерируемой случайным образом. Для удобства демонстрации примем предел одной задержки равный 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;

}

Результат возможной работы программы