Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Готовые отчеты / ОСиС. Лабораторная работа 6

.pdf
Скачиваний:
17
Добавлен:
21.11.2020
Размер:
516.53 Кб
Скачать

Федеральное агентство связи ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ

ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М. А. БОНЧ-БРУЕВИЧА» (СПбГУТ)

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

ЛАБОРАТОРНАЯ РАБОТА №6 по дисциплине «Операционные системы и сети»

на тему «Разработка многопоточной программы под Windows»

Выполнил: студент 3-го курса дневного отделения группы ИКПИ-85

Коваленко Леонид Александрович Преподаватель:

доцент кафедры ПИиВТ Дагаев Александр Владимирович

Санкт-Петербург 2020

Цель работы Разработать многопоточную программу с применением механизмов

синхронизации потоков под Windows. Постановка задачи

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

2.Написать программу на C++ для решения задачи об обедающих философах и продемонстрировать ее работу.

Ход работы

Работа выполняется в операционной системе MS Windows 10 Pro. Напишем скрипт для отображения списка запущенных процессов,

поиска по названию процессов и запуска монитора процессов (табл. 1). Таблица 1 — Файл main.ps1

while ($true) {

echo "-== Меню ==-"

echo "1. Список всех процессов." echo "2. Поиск по всем процессам." echo "3. Диспетчер задач."

$n = (Read-Host "Ввод") if ($n -LIKE "1*") {

Get-Process

}

elseif ($n -LIKE "2*") {

$substr = (Read-Host "Введите название")

$is_all = (Read-Host "Остановить все найденные процессы? (y/n)") if ($is_all -LIKE "y*") {

Get-Process -Name "$substr" | Stop-Process

}

else {

$is_all = (Read-Host "Получить подробную информацию по всем найде нным процессам? (y/n)")

if ($is_all -LIKE "y*") {

Get-Process -Name "$substr" | Format-List *

}

}

}

elseif ($n -LIKE "3*") { taskmgr

}

elseif ($n -LIKE "exit*") { exit

}

else {

echo "Ошибка ввода"

}

}

Запустим скрипт на выполнение и проверим его работу (рис. 1, 2, 3, 4).

2

Рисунок 1 — Проверка первого пункта меню

Рисунок 2 — Проверка второго пункта меню

Рисунок 3 — Проверка второго пункта меню (2) 3

Рисунок 4 — Проверка третьего пункта меню

Для того, чтобы выйти, достаточно ввести exit (и нажать Enter) или нажать Ctrl+C.

Если мы попытаемся скомпилировать код программы для решения задачи об обедающих философах из лабораторной работы №5 (Linux; язык C++) в Windows, то получим следующее сообщение об ошибке (рис. 5).

Рисунок 5 — Сообщение об ошибке компиляции Информации из Интернета достаточно, чтобы прийти к выводу об

отсутствии поддержки некоторых возможностей C++11 (в нашем случае — классов thread и mutex) компилятором GCC-10.2.0-MinGW. Есть два варианта: воспользоваться компилятором MinGW-w64 или не менять компилятор и разработать свои классы thread и mutex (классы-обертки над функциями WinAPI). Попробуем второй вариант.

Напишем программу на C++ для решения задачи об обедающих философах (табл. 2, 3).

4

Таблица 2 — Файл invoke.h1 (источник: meganz/mingw-std-threads)

#ifndef MINGW_INVOKE_H_ #define MINGW_INVOKE_H_

#include <type_traits> // For std::result_of, etc. #include <utility> // For std::forward

#include <functional> // For std::reference_wrapper

namespace mingw_stdthread { namespace detail {

// For compatibility, implement std::invoke for C++11 and C++14 #if __cplusplus < 201703L

template<bool PMemFunc, bool PMemData> struct Invoker {

template<class F, class... Args>

inline static typename std::result_of<F(Args...)>::type invoke (F&& f, Args&&... args) {

return std::forward<F>(f)(std::forward<Args>(args)...);

}

}; template<bool>

struct InvokerHelper;

template<>

struct InvokerHelper<false> { template<class T1>

inline static auto get (T1&& t1) -> decltype(*std::forward<T1>(t1)) { return *std::forward<T1>(t1);

}

template<class T1>

inline static auto get (const std::reference_wrapper<T1>& t1) -> decltype(t1.get()) {

return t1.get();

}

};

template<>

struct InvokerHelper<true> { template<class T1>

inline static auto get (T1&& t1) -> decltype(std::forward<T1>(t1)) { return std::forward<T1>(t1);

}

};

template<>

struct Invoker<true, false> {

template<class T, class F, class T1, class... Args>

inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ decltype((InvokerHelper<std::is_base_of<T,typename

std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f) (std::forward<Args>(args)...)) {

return (InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f) (std::forward<Args>(args)...);

}

};

template<>

struct Invoker<false, true> {

template<class T, class F, class T1, class... Args>

inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\

1В нашем случае этот файл нужен для получения возможности задания переменного числа аргументов (параметров функции потока) в конструкторе Thread

5

decltype(InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f) {

return InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f;

}

};

template<class F, class... Args> struct InvokeResult {

typedef Invoker<std::is_member_function_pointer<typename std::remove_reference<F>::type>::value,

std::is_member_object_pointer<typename std::remove_reference<F>::type>::value &&

(sizeof...(Args) == 1)> invoker;

inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...)) {

return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...);

}

};

template<class F, class...Args>

auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...)) {

return InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...);

}

#else

using std::invoke; #endif

}// Namespace "detail"

}// Namespace "mingw_stdthread"

#endif

Таблица 3 — Файл main.cpp

#include "invoke.h" #include <iostream> #include <functional> #include <stdexcept> #include <Windows.h>

// Класс-обертка "Поток" class Thread {

public:

// Конструктор

template < typename _Callable, typename..._Args >

Thread(_Callable && callable, _Args && ...args) { mThreadArgument = [&callable, &args...] () {

mingw_stdthread::detail::invoke(callable, args...);

};

mHandle = CreateThread(NULL, 0, run<decltype(mThreadArgument)>, &mThreadArgument, 0, NULL);

if (mHandle == 0) {

throw std::system_error(GetLastError(), std::generic_category());

}

}

// Деструктор ~Thread() {

if (this->joinable()) { TerminateThread(mHandle, 0); CloseHandle(mHandle);

}

6

}

// Отделение потока выполнения от объекта потока void detach() {

if (this->joinable()) {

if (!CloseHandle(mHandle)) {

throw std::system_error(GetLastError(), std::generic_category());

}

mHandle = INVALID_HANDLE;

} else throw std::invalid_argument("joinable() == false!");

}

// Блокировка текущего потока до тех пор, пока поток, обозначенный *this, не завершит свое выполнение

void join() {

if (this->joinable()) {

if (WaitForSingleObject(mHandle, INFINITE) != WAIT_OBJECT_0) { throw std::system_error(GetLastError(),

std::generic_category());

}

mHandle = INVALID_HANDLE;

} else throw std::invalid_argument("joinable() == false!");

}

// Проверка, идентифицирует ли объект Thread активный поток выполнения bool joinable() {

return mHandle != INVALID_HANDLE;

}

private:

//Идентификатор дескриптора потока HANDLE mHandle;

//[Константа] Неверный идентификатор дескриптора потока static constexpr HANDLE INVALID_HANDLE = nullptr;

//Функция-обертка для аргумента потока

std::function<void()> mThreadArgument;

//Функция потока template < class T >

static DWORD WINAPI run(void * threadArgument) { (*(T*) threadArgument)();

return 0;

}

//Конструктор копирования запрещен

Thread(const Thread & ) = delete; // Оператор копирования запрещен

Thread & operator = (const Thread & ) = delete;

};

// Класс-обертка "Мьютекс" class Mutex {

public:

//Конструктор Mutex() {

mHandle = CreateMutexA(NULL, FALSE, NULL); if (mHandle == 0) {

throw std::system_error(GetLastError(), std::generic_category());

}

}

//Деструктор

~Mutex() {

7

CloseHandle(mHandle);

}

//Захват мьютекса с ожиданием void lock() {

if (WaitForSingleObject(mHandle, INFINITE) != WAIT_OBJECT_0) {

throw std::system_error(GetLastError(), std::generic_category());

}

}

//Попытка захвата мьютекса без ожидания

bool tryLock() {

return WaitForSingleObject(mHandle, 0) == WAIT_OBJECT_0;

}

// Освобождение мьютекса void unlock() {

if (!ReleaseMutex(mHandle)) {

throw std::system_error(GetLastError(), std::generic_category());

}

}

private:

//Идентификатор дескриптора мьютекса HANDLE mHandle;

//Конструктор копирования запрещен Mutex(const Mutex & ) = delete;

//Оператор копирования запрещен

Mutex & operator = (const Mutex & ) = delete;

};

// Класс "Вилка" class Fork { public:

// Конструктор

Fork(std::string name) : mName(name) {}

//Ожидание и взятие вилки void take() {

mMutex.lock();

}

//Попытка взятия вилки bool tryTake() {

return mMutex.tryLock();

}

//Освобождение вилки

void put() { mMutex.unlock();

}

// Получение условного названия вилки std::string getName() const {

return mName;

}

private:

//Мьютекс Mutex mMutex;

//Условное название вилки std::string mName;

};

8

// Класс "Философ" class Philosopher { public:

// Конструктор

Philosopher(const std::string &name, Fork &leftFork, Fork &rightFork)

:mName(name), mLeftFork(&leftFork), mRightFork(&rightFork) {}

//Функция потока

void run(int iteration) {

std::string in = mName + " философ начал кушать\n"; std::string out = mName + " философ приостановил трапезу\n"; while (iteration > 0) {

//Думаем

wait();

//Берем две вилки или блокируемся mLeftFork->take();

if (!mRightFork->tryTake()) { mLeftFork->put(); continue;

}

//Обедаем

std::cout << in; std::cout.flush(); wait();

std::cout << out; std::cout.flush();

// Кладем на стол обе вилки mLeftFork->put();

wait(); mRightFork->put(); wait(); --iteration;

}

}

private:

//Ожидание milliseconds миллисекунд void wait(int milliseconds = 100) const {

Sleep(milliseconds);

}

//Имя философа

std::string mName;

// Левая и правая вилки

Fork *mLeftFork, *mRightFork;

};

int main() {

using namespace std;

std::cout << "[Старт] Начался обед двух философов" << std::endl;

{

Fork f1("#1"), f2("#2");

Philosopher p1("#1", f1, f2), p2("#2", f2, f1);

Thread thread1(&Philosopher::run, &p1, 2);

Thread thread2(&Philosopher::run, &p2, 2);

thread1.join();

thread2.join();

}

std::cout << "[Финиш] Два философа пообедали" << std::endl; std::cout << "[Старт] Начался обед пяти философов" << std::endl;

{

9

Fork f1("#1"), f2("#2"), f3("#3"), f4("#4"), f5("#5"); Philosopher p1("#1", f1, f2), p2("#2", f2, f3), p3("#3", f3, f4),

p4("#4", f4, f5), p5("#5", f5, f1);

Thread thread1(&Philosopher::run, &p1, 2);

Thread thread2(&Philosopher::run, &p2, 2);

Thread thread3(&Philosopher::run, &p3, 2);

Thread thread4(&Philosopher::run, &p4, 2);

Thread thread5(&Philosopher::run, &p5, 2);

thread1.join();

thread2.join();

thread3.join();

thread4.join();

thread5.join();

}

std::cout << "[Финиш] Пять философов пообедали" << std::endl; return 0;

}

Алгоритм решения аналогичен алгоритму решения на основе монитора2. (Недостатком является отсутствие решения проблемы голодания: один из философов (или несколько) может долго ждать своих соседей.)

Запустим программу на выполнение и проверим её работу (рис. 6).

Рисунок 6 — Работа алгоритма решения задачи об обедающих философах В данном случае мы использовали C++, а не Си, так как задача лежит

ближе к ООП, нежели к чисто процедурной парадигме.

2https://ru.wikipedia.org/wiki/Задача_об_обедающих_философах#Решение_на_основе_монитора

10