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