Добавил:
Факультет ИКСС, группа ИКВТ-61 Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
LAB / Миронов / Lab_5.docx
Скачиваний:
27
Добавлен:
20.02.2019
Размер:
135.48 Кб
Скачать

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

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

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

(СПбГУТ)

ОТЧЁТ

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

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

Выполнили: студенты группы ИКВТ-61, Миронов П.В., Гарифулина Т.С.

Принял: доцент кафедры ПИиВТ Дагаев А.В.

Цель: разработать многопоточное приложение под Windows.

Средства разработки: Для выполнения данной работы использовались: Операционная система Windows 10, Microsoft Visual C#.

Основные определения:

  1. Процесс – это некоторая часть работы ОС, обладающая уникальным идентификационным номером – id,  и адресное пространство.  Адресное пространство – некоторый список адресов в памяти, с которыми происходит работа этого процесса. С другими адресами процессу приходится работать через системный вызов. Одна программа может включать как несколько процессов, так и один, причем последнее используется наиболее часто. Разбиение на процессы позволяет распараллелить задачи, благодаря чему ускорить работу, но в большинстве случаев для этого проще и выгоднее использовать потоки, которые намного быстрее взаимодействуют друг с другом и обладают рядом других положительных моментов, что и привело к меньшей используемости процессов.

  2. Поток – это часть уже самого процесса, выполняющая определенный список действий. У каждого процесса есть как минимум один поток, и их увеличение обеспечивает распараллеливание процесса. Чем выгодней такое  увеличение потоков внутри процесса увеличения количества самих процессов? Каждый поток, как часть процесса, имеет доступ ко всему адресному пространству процесса, ко всем его устройствам и переменным. Поэтому взаимодействие двух отдельных потоков реализуется очень просто и не требует обращения к системе. По этой причине использование потоков более распространено, чем процессов.

Описание задания:

Для выполнения лабораторной была выбрана в качестве примера задача о кольцевом буфере. Потоки производители и потребители разделяют кольцевой буфер, состоящий из 100 ячеек. Производители передают сообщение потребителям, помещая его в конец очереди буфера. Потребители сообщение извлекают из начала очереди буфера.

Цель:

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

Описание работы:

Пусть для определенности буфер - это целочисленный массив из 100 элементов. Задача обладает двумя критическими секциями. Первая критическая секция связана с операциями чтения-записи нескольких потоков в общий буфер. Вторая критическая секция определяется тем. что буфер являются конечным, запись должна производиться только в те ячейки, которые являются свободными или уже прочитаны потоками-читателями (условная взаимная синхронизация).

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

Для условной синхронизации воспользуемся двумя семафорами. Значение первого семафора показывает, сколько ячеек в буфере свободно. Ячейка свободна, когда в нее еще не осуществлялась запись или ячейка быта прочитана. Значение второго семафора показывает, сколько ячеек в буфере занято. Естественно, операция записи не может быть выполнена, пока количество занятых ячеек равно 100 (или количество свободных ячеек равно 0), и операция чтения не может быть выполнена, пока количество свободных ячеек равно 100 (или количество занятых ячеек равно 0). Для блокировки потока воспользуемся: условиями, заключенными в скобки, исходя из особенностей поведения семафоров.

Схема потоков

Главный поток

Поток-производитель

Поток-потребитель

Создание потоков производителей и потоков-потребителей

Запуск стартовой функции потока-потребителя.

Бесконечный цикл

{создать элемент для буфера;

Закрыть (мьютекс 1);

Семафор Свободно --;

Запись в буфер;

Семафор Занято ++;

Открыть (мьютекс 1);

}

Бесконечный цикл

{закрыть (мьютекс 2)

Семафор Занято --;

Чтение из буфера;

Семафор Свободно ++;

Открыть (мьютекс 2);

Обработка прочитанного;

}

Ход работы:

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

using System;

using System.Threading;

namespace Example2

{

public class Producer //класс производитель

{

private int num; //объявление переменных счетчика

public Producer(int anum)

{

num = anum;

}

//процесс, пополняющий буфер

public void ProducerThread()

{

Console.WriteLine("thread {0} (producer): start!", num);

while (true) //цикл заполнения

{

Example2.hSemEmpty.WaitOne();

Example2.hMutexD.WaitOne();

Random rnd = new Random();//создание рандомной переменной

Example2.buf[Example2.rear] = rnd.Next() % Example2.n; //присвоение буферу рандомного числа

Console.WriteLine("producer {0}: data = {1} to {2}", num, Example2.buf[Example2.rear], Example2.rear); //вывод на экран присвоенного

Example2.rear = (Example2.rear + 1) % Example2.n;

Thread.Sleep(1000);

Example2.hSemFull.Release(1);

}

}

}

public class Consumer //создание класса потребителя

{

private int num; //объявление переменных счетчика

public Consumer(int anum)

{

num = anum;

}

//процесс, берущий данные из буфера

public void ConsumerThread()

{

int data;

Console.WriteLine("thread {0} (consumer): start!", num); //вывод на экран запущенного потока

while (true)

{

Example2.hSemFull.WaitOne();

Example2.hMutexF.WaitOne();

data = Example2.buf[Example2.front]; //присвоение данных производителя - потребителю

Console.WriteLine("consumer {0}: data = {1} from {2}", num, data, Example2.front); //вывод на экран полученных данных

Example2.front = (Example2.front + 1) % Example2.n;

Thread.Sleep(1000);

Example2.hMutexF.ReleaseMutex();

Example2.hSemEmpty.Release(1);

}

}

}

class Example2

{

public static int[] x;

public static int n = 100; // длина буфера

const int m = 7; // количество производителей

const int k = 3; // количество потребителей

public static Mutex hMutexD = new Mutex();//создание мьютекса для производителя

public static Mutex hMutexF = new Mutex();//создание мьютекса для потребителя

public static int[] buf = new int[n]; //копирование длины буфера

public static int front = 0;

public static int rear = 0;

public static Semaphore hSemFull = new Semaphore(0, n); //создание семафора для производителя

public static Semaphore hSemEmpty = new Semaphore(n, n); //создание семафора для потребителя

static void Main(string[] args)

{

int i;с //переменная счетчика

x = new int[k + m];

Thread[] threadDelegates = new Thread[k + m]; //запуск потоков производителей и потребителей

for (i = 0; i < k; i++)//передача информации потребителю

{

Consumer thWithNum = new Consumer(i);

threadDelegates[i] = new Thread(new ThreadStart(thWithNum.ConsumerThread));

threadDelegates[i].Start();

}

for (i = k; i < k + m; i++)//создание информации производителем

{

Producer thWithNum = new Producer(i);

threadDelegates[i] = new Thread(new ThreadStart(thWithNum.ProducerThread));

threadDelegates[i].Start();

}

for (i = 0; i < k + m; i++)

threadDelegates[i].Join();

hMutexF.Close();//закрытие мьютекса производителя

hMutexD.Close();//закрытие мьютекса потребителя

}

}

}

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