Параллельная обработка односвязных кольцевых списков в памяти ОС Windows

 

Министерство образования и науки Украины

Харьковский национальный университет им. В.Н. Каразина

Факультет компьютерных наук

Кафедра искусственного интеллекта и программного обеспечения






Курсовой проект по дисциплине

Системное программирование и операционные системы

Тема:

Параллельная обработка односвязных кольцевых списков в памяти ОС Windows






Исполнитель: студентка гр. КС-31

Е.В. Кунина

Руководитель: ст. преподаватель

О.М. Горбань






Харьков 2012


СОДЕРЖАНИЕ


Введение

. Аналитический обзор

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

.2 Общий обзор методов реализации задачи

. Выполнение проекта

.1 Функционал кольцевого списка

.2 Описание функций API для работы с пулом памяти в ОС Windows и их роль в проекте

.3 Средства создания потоков

.4 Порядок работы программы

2.5 Набор тестов для отладки программы и скриншоты результатов8

Выводы

Использованные источники информации

Приложение А. Задание на выполнение КП

Приложение Б. Листинг программы


ВВЕДЕНИЕ


На основании полученного задания, приведенного в Приложении А, можно сформулировать ряд целей и задач выполнения данного курсового проекта, которые описаны ниже.

Цели:

-ознакомиться с такими понятиями, как куча (пул памяти), связный список, синхронизация потоков;

-приобрести навыки написания многопоточных программ, обеспечивающих исключение возможности перекрытия разных потоков друг другом.

Задачи:

-найти необходимую справочную информацию об особенностях организации связных списков и использования функций API для работы с пулом памяти в ОС Windows;

-реализовать программно структуру списка и его функциональность;

дополнить программу 5-ью потоками: первый инициализирует список и удаляет кучу после ее использования, второй и третий добавляет элементы в список и удаляет их соответственно, четвертый изменяет существующие элементы, пятый выводит содержимое списка на экран;

обеспечить исключение возможности перекрытия разных потоков друг другом;

разработать ряд тестов для проверки корректности работы программы.

Связный список - структура данных <#"justify">-главный, инициализирующий структуру списка и освобождающий после его использования память;

-поток, добавляющий элементы в список;

поток, удаляющий элементы из списка;

поток, изменяющий существующие элементы;

поток, читающий информацию списка (выводящий список на экран).


.2 Общий обзор методов реализации задачи


Таким образом, первым этапом написания программы стала реализация структуры списка, а также функций, работающих с ним. Главная задача - синхронизация потоков, работающих со списком. Для этого я использовала выделение дополнительной кучи для списка. Это несет в себе ключевой смысл: каждый раз, когда какой либо поток получает доступ к списку (а значит и к куче), то он как критический раздел блокируется, а значит любой другой поток, также пытающийся получить доступ к списку будет приостановлен до момента его освобождения. Более того в программе мною были использованы методы обеспечения многопоточности.

2. ВЫПОЛНЕНИЕ ПРОЕКТА


.1 Функционал кольцевого списка


К структуре списка применимы следующие методы:

-InitialList - инициализация списка;

-AddElem - добавление узла в конец списка;

EraseElem - удаление элемента из списка; так как реализованный мною список имеет всего 2 поля: указатель на следующий элемент и целое число (непосредственно данные), то поиск элемента для его удаления производится по этому самому числу, при эьтом считается, что элементы в списке не повторяются (для формализации задачи);

ChangeElem - изменение данных уже существующего узла; поиск элементы аналогичен поиску при удалении;

Print - вывод на экран всех узлов списка.


.2 Описание функций API для работы с пулом памяти в ОС Windows и их роль в проекте


В постановке задачи есть немаловажный пункт - обеспечение исключения возможности перекрытия разных потоков друг другом. Реализовывать его предлагается посредством создания под список отдельной кучи, а все операции со списком осуществлять при помощи функций API работы с пулом памяти. В своей программе я использовала следующие функции:

-HeapCreate. Создает дополнительную кучу в процессе. Принимает 3 параметра: параметр fdwOptions модифицирует способ выполнения операций над кучей. В нем можно указать 0, НЕАР_NO_SERIALIZE, НЕАР_GENERATE_EXCEPTIONS, HEAP_CREATE_ENABLE_EXECUTE или комбинацию этих флагов. Второй параметр функции HeapCreate - dwInitiallSize - определяет количество байтов, первоначально передаваемых куче. При необходимости функция округляет это значение до ближайшей большей величины, кратной размеру страниц. И последний параметр, dwMaximumSize, указывает максимальный объем, до которого может расширяться куча (предельный объем адресного пространства, резервируемого под кучу). Если он больше 0, вы создадите кучу именно такого размера и нс сможете его увеличить. А если этот параметр равен 0, система резервирует регион и, если надо, расширяет его до максимально возможного объема. При успешном создании кучи HeapCreate возвращает описатель, идентифицирующий новую кучу. В своей программе этой функцией я создавала кучу для объекта структуры кольцевого списка.

HeapAlloc. Выделяет блок памяти из кучи. Я использовала эту функцию при создании нового элемента списка. Принимает 3 параметра: параметр hHeap идентифицирует описатель кучи, из которой выделяется память. Параметр dwBytes определяет число выделяемых в куче байтов. Параметр fdwFlags позволяет указывать флаги, влияющие на характер выделения памяти. В настоящее время поддерживается только три флага: HEAP_ZERO_MEMORY, HEAP_GENERATE_EXCEPTIONS и HEAP_NO_SERIALIZE.


Примечание. Для выделения больших блоков памяти (от 1 Мб) рекомендуется использовать функцию VirtualAlloc, а не функции, оперирующие с кучами.


-HeapFree. Служит для освобождения блока памяти. В реализованной программе используется при удалении элемента. Принимает 3 параметра: параметр hHeap идентифицирует кучу, а параметр pvMem сообщает адрес блока. Параметр fdwFlags принимает два значения: 0 или HEAP_NO_SERIALIZE.

-HeapDestroy. Уничтожает кучу вместе со всеми данными, которые в ней хранятся. Поэтому при ее использовании необходимо удостовериться, что ни один из процессов больше не будет использовать эти данные. В противном случае будет выброшено исключение или программа закончится некорректно. Принимает всего 1 параметр hHeap, который идентифицирует кучу.

Следующие две функции - HeapLock и HeapUnlock - используются парно. Они предназначены для синхронизации потоков. После успешного вызова HeapLock поток, который вызывал эту функцию, становится владельцем указанной кучи. Если другой поток обращается к этой куче, указывая тот же описатель кучи, система приостанавливает его выполнение до тех пор, пока куча не будет разблокирована вызовом HeapUnlock. Функции HeapAlloc, HeapSize, HeapFree и другие - все обращаются к HeapLock и HeapUnlock, чтобы обеспечить последовательный доступ к куче. Поэтому явно в коде они могут быть не прописаны.


.3 Средства создания потоков


Создание потока осуществляется с помощью следующей функции:


uintptr_t _beginthread(( *start_address )( void * ),stack_size,*arglist

);

Первый аргумент это указатель на функцию, которая является точкой входа для самого потока. Сама функция должна выглядеть как:

myThread(void* pParams)

{

. . .

}


Имя конечно же может быть любое. Второй аргумент это начальный размер стека. Почти в любом случае его можно указать как 0. Тогда размер стека будет равен тому же, что и у главной задачи процесса. С третьим все ещё легче, это аргументы функции потока, которые вполне могут быть как NULL. Или же выглядеть как (void*)(pParam) в случае с первой функцией. Для корректной работы вышеописанных функций требуется подключение библиотеки <process.h>.


.4 Порядок работы программы


Основной поток программы создает список и инициализирует его. После чего запускаются 4 потока. Первый пытается по очереди добавить в список элементы от 1 до 9, второй - удалить аналогичные узлы, третий - изменить аналогичные элементы путем прибавления к ним 1000, а четвертый - вывести на экран содержимое списка (10 попыток). Все потоки используют соответствующие функции работы со списком. В силу особенностей работы многопоточных приложений, при каждом его запуске результаты работы разные, так как потоки выполняются совершенно произвольно, поэтому и последовательность действий непредсказуема.


2.5 Набор тестов для отладки программы и скриншоты результатов


При выполнении программы возникают такие тестовые ситуации, как попытка распечатать пустой список, попытка изменения или удаления несуществующего элемента или элемента пустого списка. Также показательными с точки зрения корректности работы приложения являются моменты удаления/изменения последнего или первого элементов. Конечно же, основным моментом тестирования является исключение перекрытия потоков. На все эти тесты успешно отвечает лишь многократный запуск приложения. Почему многократный - потому что в силу особенностей работы многопоточных приложений, при каждом его запуске результаты работы разные, так как потоки выполняются совершенно произвольно, поэтому и последовательность действий непредсказуема.

Итак вот скриншоты результатов 3 запусков программы, которые говорят о корректности ее работы:


Рисунок 1. Результат работы программы


Как видно из Рисунка 1, первым делом поток 1 добавил в список узел со значением 1. После чего поток 3 изменил его на узел 1001. Остальные попытки изменить узлы списка не увенчались успехом, так как элементы 2-9 в список добавлены не были. На следующем этапе программы критическую секцию захватил поток 4 и 10 раз вывел содержимое списка на экран. Последующие 10 попыток потока 2 удалить элементы успешными не были, так как таких узлов в списке нет (узел 1 был изменен на 1001, остальные еще не были добавлены). Далее управление снова передалось потоку 1 и он добавил остальные элементы в список. Контрольный вывод списка на экран соответствует ожиданиям. После завершения всех потоков главный поток удалил кучу.


Рисунок 2. Результат работы программы


На Рисунке 2 видно, что в начале выполнения поток 1 получил доступ к куче со списком и успешно добавил в него 10 элементов. Далее поток 4 2 раза распечатал список. После этого все элементы были удалены потоком 2, из-за чего 10 попыток потока 3 изменить элементы и 8 попыток потока 4 вывести их на экран не удались, так список был пуст. После завершения всех потоков главный поток удалил кучу.

Работа программы при 3-ем запуске полностью совпала с 1-ым случаем, что отражено на Рисунке 3.


Рисунок 3. Результат работы программы

пул поток связной список


ВЫВОДЫ


Чтобы подытожить выполнение данного курсового проекта хотелось бы сделать ряд выводов. Во-первых, все цели данной работы были достигнуты путем пошагового выполнения поставленных в соответствии с ними задач. Я подробно ознакомилась с такими понятиями как куча, связные списки, многопоточность, овладела навыками написания многопоточных программ, требующих синхронизации отдельных потоков, а также навыками составления ряда тестов при отладке подобных программ. Программа, написанная мною в соответствии с поставленной задачей, прошла все заявленные тесты успешно, что говорит о корректности ее работы. Хотелось бы особенно выделить тот факт, что метод синхронизации потоков путем использования дополнительной кучи в процессе оказался для меня неожиданным. Но разобравшись в его особенностях, я нашла его весьма удобным и, как следствие, полезным.


ИСПОЛЬЗОВАННЫЕ ИСТОЧНИКИ ИНФОРМАЦИИ


1. Cormen, Leiserson, Rivest, and Stein. Introduction to Algorithms, 2nd edition. The MIT Press, 2001

2. #"justify">ПРИЛОЖЕНИЕ А


Задание на КП


1.Ознакомиться со свойствами и особенности обработки списочных структур.

2.Изучить функции API для работы с пулом памяти в ОС Windows

.Разработать и реализовать программу в соответствии с условием задачи.

.Разработать ряд тестов для демонстрации правильности работы программы.

.Подготовить отчет по курсовому проекту.

Демонстрационная программа должна содержать:

-главный, инициализирующий структуру списка и освобождающий после его использования память;

-поток, добавляющий элементы в список;

поток, удаляющий элементы из списка;

поток, изменяющий существующие элементы;

поток, читающий информацию списка (выводящий список на экран).


ПРИЛОЖЕНИЕ Б


Листинг программы


//---------------------------------------------------------------------------

//HEADER.H

//---------------------------------------------------------------------------struct node //узел списка, состоящий из целого числа и указателя на следующий элемент

{number;node *next;

}Node, *pnode;struct list //структура односвязного кольцевого списка

{nodes; //кол-во узлов в списке*end; //указатель на последний элемент в списке*beg; //указатель на первый элемент в списке

}List, *pList;struct params //структура для передачи параметров потоку

{*pq; //указатель на списокhp; //указатель на кучу

}Params;InitialList(List *pq, HANDLE hp); //инициализация спискаAddElem(List *pq, HANDLE hp, int n); //добавление узла с number =

n в конец спискаEraseElem(List *pq, HANDLE hp, int n); //удаление узла с number =

nChangeElem(List *pq, HANDLE hp, int o, int n); //изменение узла с

number=o на узел с number=nPrint(const List *pq, HANDLE hp); //вывод всех узлов списка на

экранThreadAdd(void *p); //поток добавления узловThreadErase(void *p); //поток удаления узловThreadChange(void *p); //поток изменения узлов

void ThreadPrint(void *p); //поток вывода всех узлов списка на экран

//---------------------------------------------------------------------------

//LFUN.CPP - содержит тела функций работы со списком

//---------------------------------------------------------------------------

#include <stdafx.h>

#include <stdio.h>

#include <malloc.h>

#include <stdlib.h>

#include <windows.h>

#include <iostream>

#include <string.h>

#include <string>

#include "header.h"namespace std;

InitialList(List *pq, HANDLE hp) //инициализация списка

{= HeapCreate(0, 0x1000, 0x10000); //создание кучи для списка>beg = NULL; //инициализация указателей начала и конца списка>end = NULL;>nodes = 0; //инициализация счетчика узловhp;

}


bool AddElem(List *pq, HANDLE hp, int n) //добавление узла с number =

n в конец списка

{fl = HeapLock(hp); //блокировка доступа к куче списка других

потоков(fl == 0) //если блокировать не удалось

{<< "adding failed" << endl;false;

}*p_new;_new = (pnode)HeapAlloc(hp, HEAP_ZERO_MEMORY, 10);

//выделение памяти в куче под новый узел(p_new == NULL) //если выделить не удалосьfalse;_new->number = n; //инициализация элементов узла_new->next = pq->beg; //так как добавление производится в конец, то

указатель на след. узел

// равен указателю на начало(pq->nodes == 0) //если список еще пуст

{>beg = pq->end = p_new;_new->next = pq->beg;

}//в противном случае

{>end->next = p_new;>end = p_new;

}>nodes++; //инкремент счетчика узлов<< n << " added" << endl;(hp); //отмена блокировки кучи для других потоков

return true;

}

EraseElem(List *pq, HANDLE hp, int n) //удаление узла с number = n

{fl = HeapLock(hp); //блокировка доступа к куче списка других

потоков(fl == 0) //если блокировать не удалось

{<< "erase failed" << endl;false;

}*curr;*lcurr;(pq->nodes == 0) //если список пуст

{<< "list is empty, erase failed" << endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоковfalse;

}= pq->beg; //в противном случае= pq->end;(curr->number == n) //если первый элемент - искомый

{(pq->nodes == 1) //при этом единственный

{>beg = NULL; //регенерация указателей и счетчика объектов в

списке

pq->end = NULL;>nodes = 0;

HeapFree(hp,0,curr); //освобождение блока памяти<< n << " erased" << endl;(hp); //отмена блокировки кучи для других потоковtrue;

}>next = curr->next; //при этом не единственный>beg = curr->next;(hp,0,curr); //освобождение блока памяти>nodes--; //декремент счетчика объектов<< n << " erased" << endl;(hp); //отмена блокировки кучи для других потоковtrue;

}(pq->nodes == 1) //если элемент единственный в списке и искомым не

является

{<< "there is no such node in list, erase failed" << endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоков

return false;

}= curr;= curr->next;(curr != pq->beg) //поиск элемента

{(curr->number != n) //если текущий не является искомым - двигаемся

дальше

{= curr;= curr->next;

}//если текуший равен искомому

{

if(curr == pq->end)>beg = lcurr;>next = curr->next;(hp,0,curr); //освобождение блока памяти>nodes--; //декремент счетчика объектов<< n << " erased" << endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоков

return true;

}

}<< "there is no such node in list, erase failed" << endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоков

return false;

}

ChangeElem(List *pq, HANDLE hp, int o, int n) //изменение узла с

number=o на узел с number=n

{fl = HeapLock(hp); //блокировка доступа к куче списка других

потоков(fl == 0) //если блокировать не удалось

{<< "change failed" << endl;false;

}*curr;(pq->nodes == 0) //если список пуст

{<< "list is empty, change failed" << endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоков

return false;

}= pq->beg;it_count = pq->nodes;(it_count > 0) //поиск элемента

{

if(curr->number != o) //если не нашли - движение дальше по списку

{= curr->next;

}//если нашли

{>number = n; //изменение значения элемента

cout << o << " changed to " << n << endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоков

return true;

}_count--;

}<< "there is no such node in list, change failed" << endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоков

return false;

}

Print(const List *pq, HANDLE hp)

{fl = HeapLock(hp); //блокировка доступа к куче списка других

потоков(fl == 0) //если блокировать не удалось

{<< "print failed" << endl;false;

}*curr;(pq->nodes == 0) //если список пуст

{<< "list is empty, there is nothing to print" << endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоков

return false;

}= pq->beg;<< curr->number;= curr->next;

while(curr != pq->beg) //вывод всех элементов списка на экран

{<< ' ' << curr->number;= curr->next;

}<< endl;

HeapUnlock(hp); //отмена блокировки кучи для других потоковtrue;

}

//---------------------------------------------------------------------------

//MAIN.CPP - тела главного и второстепенных потоков

//---------------------------------------------------------------------------

#include <stdafx.h> //подключение библиотек

#include <stdio.h>

#include <malloc.h>

#include <stdlib.h>

#include <windows.h>

#include <iostream>

#include "header.h"

#include <tchar.h>

#include <string>

#include <stdlib.h>

#include <string.h>

#include <assert.h>

#include <process.h>

#include <time.h>

#include <Windows.h>namespace std;


int c = 0; //счетчик завершившихся потоков

ThreadAdd(void *p) //поток добавления узлов

{counter = 1;*pp = new Params();

pp = (Params*) p;(counter < 10) //добавление в указанный в параметрах список

элементов от 0 до 9

{(pp->pq,pp->hp,counter);

counter++;

}++; //инкремент счетчика завершившихся процессов

_endthread();

}

ThreadErase(void *p) //поток удаления элементов

{counter = 1;*pp = new Params();

pp = (Params*) p;(counter < 10) //удаление узла из указанного в параметрах списка

со значениями от 0 до 9

{(pp->pq,pp->hp,counter);

counter++;

}++; //инкремент счетчика завершившихся процессов

_endthread();

}

ThreadChange(void *p) //поток изменения элементов

{counter = 1;*pp = new Params();

pp = (Params*) p;(counter < 10) //изменение узла указанного в параметрах списка со

значениями от 0 до 9 путем прибавления к ним 1000

{(pp->pq,pp->hp,counter,counter+1000);

counter++;

}++; //инкремент счетчика завершившихся процессов

_endthread();

}

ThreadPrint(void *p) //поток вывода списка на экран

{counter = 0;*pp = new Params();

pp = (Params*) p;(counter < 10) //10 попыток вывести узлы списка на экран

{(pp->pq,pp->hp);

counter++;

}++; //инкремент счетчика завершившихся процессов

_endthread();

}

main()

{hp = NULL;q;= InitialList(&q,hp); //инициализация списка*p = new Params();>pq = &q;>hp = hp;

_beginthread(ThreadAdd, 2, (void*)(p)); //добавление второстепенных

потоков

_beginthread(ThreadErase, 2, (void*)(p));

_beginthread(ThreadChange, 2, (void*)(p));

_beginthread(ThreadPrint, 2, (void*)(p));(1) //ожидание завершения второстепенных потоков

{(c == 4)

{(&q,hp); //контрольное распечатывание списка

HeapDestroy(hp); //удаление кучи<< "heap destroyed" << endl;

break;

}

}

}


Министерство образования и науки Украины Харьковский национальный университет им. В.Н. Каразина Факультет компьютерных наук Кафедра искусственного инте

Больше работ по теме:

КОНТАКТНЫЙ EMAIL: [email protected]

Скачать реферат © 2017 | Пользовательское соглашение

Скачать      Реферат

ПРОФЕССИОНАЛЬНАЯ ПОМОЩЬ СТУДЕНТАМ