Добавил:
Факультет ИКСС, группа ИКВТ-61 Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

LAB / WORK_6 / лаб_раб_6

.pdf
Скачиваний:
37
Добавлен:
20.02.2019
Размер:
292.01 Кб
Скачать

ФЕДЕРАЛЬНОЕ АГЕНТСТВО СВЯЗИ

ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ

«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М.А. БОНЧ-БРУЕВИЧА»

(СПбГУТ)

Кафедра программной инженерии и вычислительной техники

Отчёт

по лабораторной работе №6 на тему:

«Разработка многопоточного приложения под Linux»

по дисциплине «Операционные системы»

Выполнил: студент группы ИКВТ-61, Козырев А.Б.

«

 

»

 

 

2018

г. ___________/А.Б. Козырев/

Принял: __к.т.н.

Дагаев А.В.

«

 

»

 

 

2018

г. ___________/_А.В. Дагаев/

САНКТ-ПЕТЕРБУРГ

2018

Задание: разработать многопоточное приложение с применением приоритетов и методов синхронизации потоков под Linux.

Объект синхронизации: семафор.

Пример использования:

Исходный код:

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <cmath>

#include <semaphore.h>

#include <unistd.h> using namespace std;

typedef struct

{

uintptr_t *a; uintptr_t length; uintptr_t sum;

} MyData;

#define N 5 #define L 20

static MyData mData;

static pthread_t myThread[N]; static sem_t sem1, sem2;

static uintptr_t *a = reinterpret_cast<uintptr_t*>( malloc (N*L*sizeof(uintptr_t)) );

void *threadWork(void *arg)

{

uintptr_t offset = reinterpret_cast<uintptr_t>(arg); uintptr_t sum = 0;

uintptr_t start = offset * mData.length; uintptr_t end = start + mData.length;

//Критическая секция №3 sem_wait(&sem1);

printf("\nThread \"%llu\" at work\n", offset + 1); for (uintptr_t i = 0; i < L/N; i++)

{

for (uintptr_t j = start + N*i; j < start + N*i + N; j++)

{

printf(" [%3llu] ", mData.a[j]);

}

puts("");

}

for (uintptr_t i = start; i < end ; i++) sum += mData.a[i];

printf ("\tSum = %llu \n", sum);

mData.sum += sum; sem_post(&sem1);

sem_post(&sem2);
puts("The array data present:"); puts("");
for (uintptr_t i = 0; i < L ; i++)
{
sem_getvalue(&sem2, &value);

//Конец критической секции №3

pthread_exit(nullptr);

}

void *createArray(void *arg)

{

uintptr_t offset = reinterpret_cast<uintptr_t>(arg); uintptr_t start = offset * mData.length;

uintptr_t end = start + mData.length;

//Критическая секция №1 sem_wait(&sem2);

for (uintptr_t i = start; i < end ; i++) a[i] = pow((i+1), 1);

int value; sem_getvalue(&sem2, &value);

while(value != 0)

{

}

sleep(1);

//Конец критической секции №1

//Критическая секция №2 sem_wait(&sem1); sem_getvalue(&sem2, &value);

if ( value == 0 )

{

for (uintptr_t j = i*N; j < i*N + N; j++)

{

printf(" [%3llu] ", mData.a[j]);

}

puts("");

}

}

sem_post(&sem1);

//Конец критической секции №2

threadWork(arg);

}

int main ()

{

mData.length = L; mData.a = a; mData.sum = 0;

void *status; sem_init(&sem1, 0, 1); sem_init(&sem2, 0, N);

for(long i=0; i < N; i++)

pthread_create(&myThread[i], nullptr, createArray, reinterpret_cast<void*>(i));

for(int i=0; i < N; i++) pthread_join(myThread[i], &status);

printf ("\n\tSum = %llu \n", mData.sum); free (a);

sem_destroy(&sem1); sem_destroy(&sem2);

return 0;

}

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

Создана глобальная структура MyData с тремя полями: *a, length, sum.

В этих атрибутах будут храниться× данные с результатами. Для указателя *a выделяется память под элементов типа int. Затем создаются объект этой структуры с идентификатором mData, а также: N переменных потока pthread_t, два семафора: sem1 и sem2.

Рассмотрим работу программы по шагам:

Функция main():

1.Инициализирует данные структуры mData;

2.Инициализирует семафор sem1 со значением 1;

3.Инициализирует семафор sem2 со значением N (кол-во потоков);

4.Создаёт N потоков через цикл for и передаёт их в функцию createArray, с аргументами в виде счётчика для идентификации этих потоков;

5.С помощью функции pthread_join() дожидается завершения каждого из потоков;

6.Выводит результат из атрибута mData.sum на стандартный поток вывода;

7.Освобождает указатель *a;

8.Уничтожает семафоры sem1, sem2.

9.Завершается с кодом «0» в случае успеха.

Функция createArray():

1.Присваивает аргумент потока в переменную offset;

2.Инициализирует начало и конец для индексирования массива;

3.Заходит в критическую секцию №1;

I.В критической секции №1 каждый поток декрементирует семафор sem2 и начинает заполнять свой сегмент массива a;

II.Получает текущее состояние семафора sem2 с помощью функции sem_getvalue() и заходит в цикл while(), где ожидает прохождения критической секции №1 последним потоком;

III.Ожидает 1 секунду для большей синхронизации по времени;

4.Заходит в критическую секцию №2;

I.В критической секции №2 каждый поток блокирует этот блок кода семафором sem1, который выступает в роли мьютекса;

II.Получает текущее значение семафора sem2;

III.Проверяет значение семафора sem2 на нуль и, если это так, то он заходит в блок условия if и вызывает функцию sem_post() для ин-

кремента семафора sem2, чтобы другие потоки не смогли зайти в этот блок;

IV. Этот единственный поток выводит заполненный массив целиком по 5 элементов на строчку, затем выводит пустую строку и освобождает семафор sem1;

V.Каждый поток так или иначе заходит в критическую секцию №2, но не каждый заходит в блок с условным оператором if, затем освобождает семафор sem1 и выходит из это критической секции;

5.Далее, каждый поток вызывает функцию threadWork(), где продолжает свою работу.

Функция threadWork():

1.Присваивает аргумент потока в переменную offset;

2.Инициализирует начало и конец для индексирования массива;

3. Заходит в критическую секцию №3 с помощью семафора sem1;

I.Выводит приветствие в поток вывода;

II.Выводит свою часть массива на экран;

III.Вычисляет сумму своих элементов массива;

IV. Выводит сумму своих элементов массива на экран;

V. Суммирует свою сумму из предыдущего пункта с общей суммой; VI. Выходит из критической секции №3;

VII. Завершает свою работу с помощью функции pthread_exit().

В результате такого алгоритма заметим следующее:

Семафор sem2 выступает в ролях: барьера в критической секции №1, условной переменной в критической секции №2.

Семафор sem1 выступает в ролях: мьютекса в критических секциях №2 и №3. Сложнее всего была реализации критической секции №2, так как требовалось обеспечить вывод массива только одним потоком, с тем условием, чтобы другие потоки успели пройти критическую секцию №1, именно для этого используется задержка sleep(1); если бы не эта задержка на 1 секунду, то другой поток мог не успеть выйти из критической секции №1, а может и несколько. Покажем пример каждого случая после корректной работы программы.

Результат работы программы:

Случай 1: задержка на 1 секунду после барьера в критической секции №1 отсутствует.

Потоки №№ 1, 2 и 5 застревают в бесконечном цикле while(), где значение value закрепилось потоками 3 и 4 в ненулевом состоянии.

Случай 2: отсутствие мьютекса перед входом в оператор if.

Никто не дожидается потока 2, другие потоки просто проскакивают мимо!

Вывод:

Врезультате мы достигли следующего:

1.Научились работать с потоками в системе Linux, также применили знания для написания программы;

2.Повторили материал по потокам ввода-вывода, стандартным конструкциям языка C++;

3.Использовали семафор в ролях: мьютекса, условной переменной, барьера

– для синхронизации одновременно выполняющихся потоков;

Соседние файлы в папке WORK_6