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

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

.docx
Скачиваний:
49
Добавлен:
20.02.2019
Размер:
97.48 Кб
Скачать

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

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

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

(СПбГУТ)

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

Отчёт

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

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

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

« » 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);

//Конец критической секции №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)

{

sem_getvalue(&sem2, &value);

}

sleep(1);

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

//Критическая секция №2

sem_wait(&sem1);

sem_getvalue(&sem2, &value);

if ( value == 0 )

{

sem_post(&sem2);

puts("The array data present:");

puts("");

for (uintptr_t i = 0; i < L ; i++)

{

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;

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

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

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

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

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

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

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

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

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

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

Функция threadWork():

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

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

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

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

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

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

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

    5. Суммирует свою сумму из предыдущего пункта с общей суммой;

    6. Выходит из критической секции №3;

    7. Завершает свою работу с помощью функции 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. Использовали семафор в ролях: мьютекса, условной переменной, барьера – для синхронизации одновременно выполняющихся потоков;

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

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