Выдержка из текста работы
Разработка приложения с несколькими обрабатывающими файл потоками, использующими взаимоисключение на основе критической секции без блокировки частей файла
Задание
приложение многопоточный взаимоисключение блокировка
Разработать приложение с тремя рабочими потоками. Первоначально по команде главный поток заполняет файл 10000 — ми трехбайтовых записей, поля каждой из которых формируются случайным образом. Каждая запись интерпретируется как цвет в модели RGB и используется для закраски квадрата, отображаемого в области рисунка диалогового окна приложения. Область рисунка окна приложения разбивается на 100Ч100 квадратов. Записи файла отображаются на матрицу квадратов области рисунка построчно. После создания файла записей главный поток прорисовывает соответствующие им квадраты в области рисунка. Далее, по соответствующей команде запускаются одновременно три рабочих потока. Первый рабочий поток упорядочивает записи файла в порядке возрастания по первому полю (интенсивность красного цвета). Второй рабочий поток упорядочивает записи по второму полю (интенсивность зеленого цвета) в порядке возрастания при равенстве у записей первых полей. Третий рабочий поток упорядочивает записи по третьему полю (интенсивность синего цвета) в порядке возрастания при равенстве у записей первых полей и вторых полей. Упорядочение производится перестановкой двух соседних записей путем циклического продвижения по записям файла каждым из потоков. Для обеспечения взаимоисключения потоков при сравнении и перестановке соседних записей использовать критическую секцию. Блокировку файла и его частей не использовать. После каждой перестановки пары соседних записей должна происходить перерисовка соответствующей пары квадратов в области рисунка окна приложения. Остановка работы потоков производится по соответствующей команде. Предусмотрите все необходимые элементы управления в интерфейсе приложения. Для лучшего наблюдения за работой потоков предусмотрите временные задержки в их работе. Управление величиной задержки вынесите в интерфейс приложения.
1. Анализ предметной области
Для разработки приложения было решено использовать среду Microsoft Visual Studio 2010 Express, так как она на данный момент является современной, наиболее оптимизированной, удобной, а также бесплатной.
Исходя из технического задания, был разработан план, содержащий основные моменты задания и позволяющий отслеживать выполнение работы.
+ интерфейс
+ обработчики событий
+ синхронизацию потоков производить посредством критических секции
+ организовать возможность отключения всех / определенного потока
Все действия по работе с цветом и синхронизацией потоков будут организованы с помощью WinAPI-функций.
2. Интерфейс приложения
Перед созданием скриншота второй и третий поток были выключены на пару минут. Поэтому мы можем наблюдать, что часть красных пикселей была вытеснена в правую часть.
1. Панель управления потоками, предоставляющая возможность включить / выключить отдельный поток
2. Панель управления задержкой.
3. Кнопка, позволяющая приостановить / продолжить работу сразу всех дочерних потоков
4. Изображение размером 100х100 отображающее ход работы потоков.
5. Индикатор хода работы потока, производящего сортировку по первой составляющей RGB-цвета.
6. Индикатор хода работы потока, производящего сортировку по второй составляющей RGB-цвета.
7. Индикатор хода работы потока, производящего сортировку по третьей составляющей RGB-цвета.
3. Исходный код программы
Проект состоит из одного модуля, т.к. код оптимизирован, минимизирован и не нуждается в разбиении на составляющие.
// course_work_027.cpp
#include «stdafx.h»
#include <stdlib.h>
#include <wingdi.h>
#include <iostream>
#include «stdlib.h»;
#include «time.h»;
#define NUM 10000 // Количество пикселей
#define MAX 16581375 // Максимальное значение RGB-цвета в int-представлении
#define Randomize() srand((unsigned) time(NULL)); // Генератор случайных значений
const short X1 = 270; // Координаты квадрата, в котором выводится ход сортировки
const short X2 = 370;
const short Y1 = 10;
const short Y2 = 110;
HWND hwnd;
HINSTANCE hInst; // Для сохранения дескриптора приложения
CRITICAL_SECTION cs; // Идентификатор критической секции
HDC hdc = NULL; // Дескриптор полотна для рисования
RECT rect; // Прямоугольник для позиционирования элементов управления
bool pause=0; // Флаг паузы
int colors[10001]; // Цвет пикселей
int delay=0; // Значение задержки
// Удобная функция для вывода сообщений
void ShowMessage (wchar_t* wch) {MessageBoxW (NULL, wch, wch, NULL);
}
// Генератор случайных чисел в диапазоне
int Random (int range_min, int range_max)
return (double) rand() / (RAND_MAX + 1) * (range_max — range_min)+ range_min;
// Генератор случайных чисел от 0 до заданного значения
int Random (int range_max)
return (double) rand() / (RAND_MAX + 1) * (range_max);
// Возвращает текст объекта по ID
wchar_t* getText (int hItem) {
int length = GetWindowTextLength (GetDlgItem(hwnd, hItem));
if (length > 0)
wchar_t* tempChar;
tempChar= (wchar_t*) GlobalAlloc (GPTR, length + 1);
GetDlgItemText (hwnd, hItem, LPWSTR(tempChar), length + 1);
return tempChar;
return 0;
// Потоки запросов
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL;
HANDLE hThread3 = NULL;
// Флаги активности потоков
bool bThrActive[3];
// Код выхода
DWORD dwExitCode;
APIENTRY WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
static wchar_t szAppName[]=L «arbora_course_27»;
MSG msg;
WNDCLASSEX wndclass;
hInst = hInstance;
// Инициализация критической секции
InitializeCriticalSection(&cs);
// Создаем потоки с флагом, «запуск ожидается»
hThread1 = CreateThread (NULL, 0,&Thread1, NULL, CREATE_SUSPENDED, NULL);
hThread2 = CreateThread (NULL, 0,&Thread2, NULL, CREATE_SUSPENDED, NULL);
hThread3 = CreateThread (NULL, 0,&Thread3, NULL, CREATE_SUSPENDED, NULL);
// Инициализируем окно
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
// Регистрируем окно
RegisterClassEx(&wndclass);
hwnd = CreateWindow (szAppName, L «Курсовая работа Арбора Сергей ПЕ-01б (Вариант 27)», WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 300,
NULL, NULL, hInstance, NULL);
ShowWindow (hwnd, nCmdShow);
UpdateWindow(hwnd);
// Создаем цикл сообщений
while (GetMessage (&msg, NULL, 0, 0))
TranslateMessage (&msg);
DispatchMessage (&msg);
// Завершение всех потоков и семафоров при выходе
TerminateThread (hThread1, dwExitCode);
TerminateThread (hThread2, dwExitCode);
TerminateThread (hThread3, dwExitCode);
CloseHandle(hThread1);
CloseHandle(hThread2);
CloseHandle(hThread3);
// Удаление критической секция
DeleteCriticalSection(&cs);
return msg.wParam;
DWORD APIENTRY Thread1 (PVOID lpParameter)
while(true) {
int k=0; // Обход массива с цветом пикселей
for (int i=0; i<100; ++i)
for (int j=0; j<100; ++j) {
Sleep(delay); // Задержка
// Если поток выключен или пауза — пропуск итерации
if (! bThrActive[0] || pause) continue;
// Вход в критическую секцию
EnterCriticalSection (&cs);
// Выводим ход работы потока в соответствующую ему квадратную область
SetPixel (hdc, X1+110+j, Y1+i, RGB (155,0,0));
// Приводим тип int к COLORREF
COLORREF cl1 = colors[k];
COLORREF cl2 = colors [k+1];
// Если текущий пиксель содержит больше цвета по которому сортирует поток чем следующий — смена местами и перерисовка
if (GetRValue(cl1) > GetRValue(cl2))
SetPixel (hdc, X1+j, Y1+i, cl2);
SetPixel (hdc, X1+j+1, Y1+i, cl1);
int x = colors[k];
colors[k] = colors [k+1];
colors [k+1] = x;
// Выход из критической секции
LeaveCriticalSection(&cs);
++k;
// Проход цикла завершен — стираем закрашенную область соответствующую данному потоку
RECT r;
r.left = X1+110;
r.top = Y1;
r.right = X2+110;
r.bottom = Y2;
InvalidateRect (hwnd,&r, true);
DWORD APIENTRY Thread2 (PVOID lpParameter)
while(true) {
int k=0;
for (int i=0; i<100; ++i) {
for (int j=0; j<100; ++j) {
Sleep(delay);
if (! bThrActive[1]) continue;
EnterCriticalSection (&cs);
SetPixel (hdc, X1+j, Y1+110+i, RGB (0,155,0));
if (! pause) {
COLORREF cl1 = colors[k];
COLORREF cl2 = colors [k+1];
if (GetGValue(cl1) > GetGValue(cl2)) {
SetPixel (hdc, X1+j, Y1+i, cl2);
SetPixel (hdc, X1+j+1, Y1+i, cl1);
int x = colors[k];
colors[k] = colors [k+1];
colors [k+1] = x;
LeaveCriticalSection(&cs);
++k;
RECT r;
r.left = X1;
r.top = Y1+110;
r.right = X2;
r.bottom = Y2+110;
InvalidateRect (hwnd,&r, true);
DWORD APIENTRY Thread3 (PVOID lpParameter)
while(true) {
int k=0;
for (int i=0; i<100; ++i) {
for (int j=0; j<100; ++j) {
Sleep(delay);
if (! bThrActive[2]) continue;
EnterCriticalSection (&cs);
SetPixel (hdc, X1+110+j, Y1+110+i, RGB (0,0,155));
if (! pause) {
COLORREF cl1 = colors[k];
COLORREF cl2 = colors [k+1];
if (GetBValue(cl1) > GetBValue(cl2))
SetPixel (hdc, X1+j, Y1+i, cl2);
SetPixel (hdc, X1+j+1, Y1+i, cl1);
int x = colors[k];
colors[k] = colors [k+1];
colors [k+1] = x;
LeaveCriticalSection(&cs);
++k;
RECT r;
r.left = X1+110;
r.top = Y1+110;
r.right = X2+110;
r.bottom = Y2+110;
InvalidateRect (hwnd,&r, true);
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
PAINTSTRUCT ps;
static HWND hCBThread[3]; // Переключатели потоков
static HWND hEditDelay; // Поле ввода задержки
static HWND hBtnDelay; // Кнопка установки новой задержки
static HWND hBtnPause; // Кнопка паузы
wchar_t szBuffer[100];
switch (iMsg)
case WM_CREATE:
GetClientRect (hwnd,&rect); // Установка Padding (отступ внутри окна)
rect.top+=10;
rect.bottom-=10;
rect.left+=10;
rect.right-=10;
// Создание элементов интерфейса
hCBThread[0] = CreateWindow (L «BUTTON», L «Поток 1», BS_AUTOCHECKBOX|WS_CHILD|WS_VISIBLE, rect.left, rect.top, 230, 30, hwnd, (HMENU) 1, hInst, NULL);
hCBThread[1] = CreateWindow (L «BUTTON», L «Поток 2», BS_AUTOCHECKBOX|WS_CHILD|WS_VISIBLE, rect.left, rect.top+25, 230, 30, hwnd, (HMENU) 2, hInst, NULL);
hCBThread[2] = CreateWindow (L «BUTTON», L «Поток 3», BS_AUTOCHECKBOX|WS_CHILD|WS_VISIBLE, rect.left, rect.top+50, 230, 30, hwnd, (HMENU) 3, hInst, NULL);
hEditDelay = CreateWindow (L «EDIT», NULL, WS_CHILD|WS_VISIBLE|WS_BORDER|ES_AUTOHSCROLL|ES_LEFT, rect.left, rect.top+100, 60, 30, hwnd, (HMENU) 12, hInst, NULL);
hBtnDelay = CreateWindow (L «BUTTON», L «Установить задержку», WS_CHILD|WS_VISIBLE, rect.left+70, rect.top+100, 160, 30, hwnd, (HMENU) 13, hInst, NULL);
hBtnPause = CreateWindow (L «BUTTON», L «Пауза», WS_CHILD|WS_VISIBLE, rect.left, rect.top+145, 230, 30, hwnd, (HMENU) 14, hInst, NULL);
// Активация кнопок
SendMessage (hCBThread[0], BM_SETCHECK, 1, 0);
SendMessage (hCBThread[1], BM_SETCHECK, 1, 0);
SendMessage (hCBThread[2], BM_SETCHECK, 1, 0);
for (int i=0; i<3; ++i) bThrActive[i] = true;
for (int i=0; i<NUM; ++i) colors[i] = (COLORREF) RGB((rand()&255), (rand()&255), (rand()&255));
colors[10000]=0;
ResumeThread(hThread1);
ResumeThread(hThread2);
ResumeThread(hThread3);
return 0;
case WM_PAINT:
GetClientRect (hwnd, &rect);
hdc = BeginPaint (hwnd, &ps);
HBRUSH hBrush;
HDC hdc;
hdc = GetDC(hwnd);
hBrush = CreateSolidBrush (RGB (0, 0, 0));
// Перерисовываем исходя из текущего отсортированного массива цвета
SelectObject (hdc, hBrush);
int k=0;
for (int i=0; i<100; ++i)
for (int j=0; j<100; ++j) {
SetPixel (hdc, X1+i, Y1+j, colors [k++]);
ReleaseDC (hwnd, hdc);
DeleteObject(hBrush);
return 0;
case WM_SIZE:
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case 1:case 2:case 3:
// Включение / Отключение выбранного потока
bThrActive [wParam-1] = (SendMessage (GetDlgItem(hwnd, wParam), BM_GETCHECK, 0, 0) == BST_CHECKED);
break;
case 13: // Установка задержки
delay = _wtoi (getText(12));
break;
case 14:
pause=! pause;
break;
return 0;
case WM_DESTROY:
PostQuitMessage (0);
return 0;
return DefWindowProc (hwnd, iMsg, wParam, lParam);
Выводы
Процесс (задача) — программа, находящаяся в режиме выполнения. В многозадачной системе реальный процессор переключается с процесса на процесс, но для упрощения модели рассматривается набор процессов, идущих параллельно (псевдопараллельно). Каждому процессу соответствует адресное пространство и одиночный поток исполняемых команд. В многопользовательских системах, при каждом обращении к одному и тому же сервису, приходится создавать новый процесс для обслуживания клиента. Это менее выгодно, чем создать квазипараллельный поток внутри этого процесса с одним адресным пространством. Преимущества использования потоков:
· Упрощение программы в некоторых случаях, за счет использования общего адресного пространства.
· Быстрота создания потока, по сравнению с процессом, примерно в 100 раз.
· Повышение производительности самой программы, т.к. есть возможность одновременно выполнять вычисления на процессоре и операцию ввода / вывода. Пример: текстовый редактор с тремя потоками может одновременно взаимодействовать с пользователем, форматировать текст и записывать на диск резервную копию.
Во время разработки программного продукта я изучил и применил на практике использование нескольких потоков в одном приложении. Это позволило увеличить быстродействие приложения, а также разграничить пользовательский интерфейс и обработку данных.
Также я познакомился и научился применять функции WinAPI для работы с файлами. Это также позволило увеличить быстродействие и упростить программный продукт, так как доступ к файлам осуществлялся напрямую через Windows.
В итоге был разработан конечный программный продукт, наглядно демонстрирующий работу многопоточного приложения. Уверен, полученные знания можно будет применять в дальнейшем для разработки более сложных проектов.
Используемая литература
1. А.В. Гордеев. Операционные системы: Учебник для вузов. 2-е изд. — СПб.: Питер, 2007. — 416 с.: ил.
2. С.В. Назаров. Операционные среды, системы и оболочки. Основы структурной и функциональной организации: Учеб.пособие. — М.: КУДИЦ-ПРЕСС, 2007. — 504 с., илл.
3. С.В. Назаров, Л.П. Гудыно, А.А. Кириченко. Операционные системы. Практикум. Под ред. С.В. Назарова — М.: КУДИЦ-ПРЕСС, 2008. — 464 с., илл.
4. В.Г. Олифер, Н.А. Олифер. Сетевые операционные системы: Учебник для вузов. 2-е изд. — СПб.: Питер, 2008. — 669 с.: ил.
5. Д.В. Иртегов. Введение в операционные системы. — 2-е изд., перераб. и доп. — СПб.: БХВ-Петербург, 2008. — 1040 с.: ил. — (Учебное пособие)
6. Х.М. Дейтел, П.Дж. Дейтел, Д.Р. Чофнес. Операционные системы. Основы и принципы: Третье издание. Пер. с англ. — М.: ООО «Бином-Пресс», 2006 г. — 1024 с.: ил.
7. Х.М. Дейтел, П.Дж. Дейтел, Д.Р. Чофнес. Операционные системы. Распределенные системы, сети, безопасность: Третье издание. Пер. с англ. — М.: ООО «Бином-Пресс», 2006 г. — 704 с.: ил.
8. Э. Таненбаум. Современные операционные системы. 2-е изд. — СПб.: Питер, 2006. — 1038 с.: ил.
9. Д. Бэкон, Т. Харрис. Операционные системы. — СПб.: Питер; Киев: Издательская группа BHV, 2004. — 800 с.: ил.
10. Джеффри Рихтер. Windows для профессионалов (программирование в Win32 API для WindowsNT 3.5 и Windows 95) /Пер. с англ. — М.: Издательский отдел «Русская редакция» ТОО «ChannelTradingLtd.», 1995. — 720 с.: ил.
11. Джеффри Рихтер. Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64-разрядной Windows /Пер. с англ. — 4-е изд. — Спб.: Питер; М.: Издательство «Русская редакция», 2008. — 722 с.: ил.
12. Ч. Петзолд. Программирование для Windows 95. Том 1: пер. с англ. — СПб.: BHV — Санкт-Петербург, 1997. — 752 с., ил.
13. Ч. Петзолд. Программирование для Windows 95. Том 2: пер. с англ. — СПб.: BHV — Санкт-Петербург, 1997. — 368 с., ил.
Размещено на