МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ “ЛЬВІВСЬКА ПОЛІТЕХНІКА”
КАФЕДРА ЕОМ
Лабораторна робота №2
з курсу
“ Системне програмне забезпечення”
ВАРІАНТ 4
Тема: Взаємодія між потоками.
Мета: Засвоїти поняття паралельного виконання «потоків» та освоїти засоби їх синхронізації. Здобути навики синхронізації «потоків» при обробці спільних даних та доступу до ресурсів в операційній системі Windows.
Теоретична частина:
Windows надає чотири об’єкти, призначені для синхронізації потоків і процесів. Три з них — мютекси, семафори і події — є об’єктами ядра, що мають дескриптори. Події використовуються також для інших цілей, наприклад, для асинхронного уведення-виведення.
Спочатку розглянемо четвертий об’єкт, а саме, об’єкт критичної ділянки коду CRITICAL_SECTION. Через простоту і продуктивність, об’єктам критичних ділянок коду надається перевага, якщо їх можливостей достатньо для того, щоб задовольнити вимоги програміста.
Об’єкт критичної ділянки коду — це ділянка програмного коду, який кожного разу повинен виконуватися тільки одним потоком; паралельне виконання цієї ділянки декількома потоками може приводити до непередбачуваних або невірних результатів.
Об’єкти CRITICAL_SECTION (CS) можна ініціалізувати і видаляти, але вони не мають дескрипторів і не можуть спільно використовуватися іншими процесами. Відповідні змінні повинні оголошуватися як змінні типу CRITICAL_SECTION. Потоки входять в об’єкти CS і покидають їх, але виконання коду окремого об’єкту CS кожного разу дозволено тільки одному потоку. Разом з тим, один і той же потік може входити в декілька окремих об’єктів CS і покидати їх, якщо вони розташовані в різних місцях програми.
Завдання:
1. Дослідити роботу програми в середовищі Visual Studio, що демонструє використання об’єктів критичних ділянок коду
2. Відповідно до варіанту модифікувати програму так, щоб замінити об’єкти синхронізації заданого числа потокі (Critical Section – 3, Semaphores – 4);
3. Проаналізувати та пояснити вміст дисплею після завершення програми.
ТЕКСТ ПРОГРАМИ:
#include "EvryThng.h"
#include <time.h>
#define DATA_SIZE 256
typedef struct msg_block_tag { /* Message block */
volatile DWORD f_ready, f_stop;
/* ready state flag, stop flag */
volatile DWORD sequence; /* Message block sequence number */
volatile DWORD nCons, nLost;
time_t timestamp;
CRITICAL_SECTION mguard; /* Guard the message block structure */
DWORD checksum; /* Message contents checksum */
DWORD data[DATA_SIZE]; /* Message Contents */
} MSG_BLOCK;
MSG_BLOCK mblock = { 0, 0, 0, 0, 0 };
HANDLE sema_h;
unsigned int WINAPI produce(void *);
unsigned int WINAPI consume(void *);
unsigned int WINAPI WriteFunc(void *);
unsigned int WINAPI ReadFunc(void *);
void MessageFill(MSG_BLOCK *);
void MessageDisplay(MSG_BLOCK *);
int value = 0;
unsigned uThreadIDs[4];
HANDLE hThreads[4];
DWORD _tmain(DWORD argc, LPTSTR argv[])
{
unsigned int Status, ThId;
HANDLE produce_h, consume_h;
sema_h = CreateSemaphore(NULL, 0, 1000, NULL);
hThreads[0] = (HANDLE)_beginthreadex(NULL, 0, ReadFunc, NULL, 0, &uThreadIDs[0]);
hThreads[1] = (HANDLE)_beginthreadex(NULL, 0, WriteFunc, NULL, 0, &uThreadIDs[1]);
hThreads[2] = (HANDLE)_beginthreadex(NULL, 0, ReadFunc, NULL, 0, &uThreadIDs[2]);
hThreads[3] = (HANDLE)_beginthreadex(NULL, 0, WriteFunc, NULL, 0, &uThreadIDs[3]);
/* Initialize the message block CRITICAL SECTION */
InitializeCriticalSection(&mblock.mguard);
/* Create the two threads */
produce_h = (HANDLE)_beginthreadex(NULL, 0, produce, NULL, 0, &ThId);
consume_h = (HANDLE)_beginthreadex(NULL, 0, consume, NULL, 0, &ThId);
/* Wait for the producer and consumer to complete */
Status = WaitForSingleObject(consume_h, INFINITE);
Status = WaitForSingleObject(produce_h, INFINITE);
DeleteCriticalSection(&mblock.mguard);
WaitForMultipleObjects(4, hThreads, TRUE, INFINITE); // без тайм-аута
CloseHandle(sema_h);
CloseHandle(hThreads[0]);
CloseHandle(hThreads[1]);
CloseHandle(hThreads[2]);
CloseHandle(hThreads[3]);
_tprintf(_T("Producer and consumer threads have terminated\n"));
_tprintf(_T("Messages produced: %d, Consumed: %d, Known Lost: %d\n"),
mblock.sequence, mblock.nCons, mblock.nLost);
return 0;
}
unsigned int WINAPI produce(void *arg)
/* Producer thread - Create new messages at random intervals */
{
srand((DWORD)time(NULL)); /* Seed the random # generator */
while (!mblock.f_stop) {
/* Random Delay */
Sleep(rand() / 100);
/* Get the buffer, fill it */
EnterCriticalSection(&mblock.mguard);
__try {
if (!mblock.f_stop) {
mblock.f_ready = 0;
MessageFill(&mblock);
mblock.f_ready = 1;
mblock.sequence++;
}
}
__finally { LeaveCriticalSection(&mblock.mguard); }
}
_endthreadex(0);
return 0;
}
unsigned int WINAPI consume(void *arg)
{
DWORD ShutDown = 0;
CHAR command, extra;
/* Consume the NEXT message when prompted by the user */
while (!ShutDown) { /* This is the only thread accessing stdin, stdout */
_tprintf(_T("\n**Enter 'c' for consume; 's' to stop; 'd' to show the date:"));
_tscanf(L"%c%c", &command, &extra);
if (command == 's') {
EnterCriticalSection(&mblock.mguard);
ShutDown = mblock.f_stop = 1;
LeaveCriticalSection(&mblock.mguard);
}
else if (command == 'c') { /* Get a new buffer to consume */
EnterCriticalSection(&mblock.mguard);
__try {
if (mblock.f_ready == 0)
_tprintf(_T("No new messages. Try again later\n"));
else {
MessageDisplay(&mblock);
mblock.nCons++;
mblock.nLost = mblock.sequence - mblock.nCons;
mblock.f_ready = 0; /* No new messages are ready */
}
}
__finally { LeaveCriticalSection(&mblock.mguard); }
}
else if (command == 'd') { /* Get a new buffer to consume */
EnterCriticalSection(&mblock.mguard);
__try {
if (mblock.f_ready == 0)
_tprintf(_T("No new messages. Try again later\n"));
else {
_tprintf(_T("\nToday`s date is: %s"),
_tctime(&(mblock.timestamp)));
mblock.nCons++;
mblock.nLost = mblock.sequence - mblock.nCons;
mblock.f_ready = 0; /* No new messages are ready */
}
}
__finally { LeaveCriticalSection(&mblock.mguard); }
}
else {
_tprintf(_T("Illegal command. Try again.\n"));
}
}
_endthreadex(0);
return 0;
}
void MessageFill(MSG_BLOCK *mblock)
{
/* Fill the message buffer, and include checksum and timestamp */
/* This function is called from the producer thread while it */
/* owns the message block mutex */
DWORD i;
mblock->checksum = 0;
for (i = 0; i < DATA_SIZE; i++) {
mblock->data[i] = rand();
mblock->checksum ^= mblock->data[i];
}
mblock->timestamp = time(NULL);
return;
}
void MessageDisplay(MSG_BLOCK *mblock)
{
/* Display message buffer, timestamp, and validate checksum */
/* This function is called from the consumer thread while it */
/* owns the message block mutex */
DWORD i, tcheck = 0;
for (i = 0; i < DATA_SIZE; i++)
tcheck ^= mblock->data[i];
_tprintf(_T("\nMessage number %d generated \n"),
mblock->sequence);
_tprintf(_T("First and last entries: %x %x\n"),
mblock->data[0], mblock->data[DATA_SIZE - 1]);
if (tcheck == mblock->checksum)
_tprintf(_T("GOOD ->Checksum was validated.\n"));
else
_tprintf(_T("BAD ->Checksum failed. message was corrupted\n"));
return;
}
unsigned int WINAPI ReadFunc(void *args)
{
WaitForSingleObject(sema_h, INFINITE);
_tprintf(_T("\ni`m 1 group thread and i my number has changed.\nNew number in 2 thread is: %d"), value);
return 0;
}
unsigned int WINAPI WriteFunc(void *args)
{ Sleep(1000);
value += 2;
printf("\nI`m 2 group thread, and i genereted a number: %d", value);
ReleaseSemaphore(sema_h, 1, NULL);
Sleep(1000);
return 0;
}
Рис.1.Результат роботи програми
ВИСНОВОК: Під час виконання лабораторної роботи засвоїв поняття паралельного виконання «потоків» та освоїв засоби їх синхронізації. Здобув навики синхронізації «потоків» при обробці спільних даних та доступу до ресурсів в операційній системі Windows.