Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабораторные. Дагаев / Операционные_системы_ЛР_№2_Отчет

.docx
Скачиваний:
1
Добавлен:
02.01.2026
Размер:
1.44 Mб
Скачать

Министерство цифрового развития, связи и массовых коммуникаций Российской Федерации

Федеральное государственное бюджетное образовательное учреждение Высшего образования «Санкт-Петербургский государственный университет телекоммуникаций им. Проф. М. А. Бонч-Бруевича» (СПбГУТ)

Факультет Информационных технологий и программной инженерии

Кафедра Программной инженерии

Лабораторная работа №2

По дисциплине: Операционные системы и сети

Выполнил студент:

Яковлев М. А. ИКПИ-32

Принял работу:

Дагаев А. В.

Дата выполнения:

«31» октября 2025 г.

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

2025 г.

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

Необходимо разработать многопоточный алгоритм, моделирующий работу алгоритма «Читатели-Писатели». Программа должна обеспечивать синхронизацию доступа к общему ресурсу (хранилищу данных) между несколькими потоками-читателями и потоками-писателями.

Перечень функций

printTimeStamp — Формирование и вывод временной метки с миллисекундной точностью

logPrint — Потокобезопасное логирование сообщений с временными метками

writer — Создание и управление потоками-писателями с синхронизацией доступа

reader — Создание и управление потоками-читателями с контролем блокировок

main — Точка входа программы, инициализация и запуск многопоточной системы

GetLocalTime — Получение системного времени с миллисекундной точностью (WinAPI)

Sleep — Приостановка выполнения потока на заданное время

Std::lock_guard — Автоматическое управление блокировками мьютекса

Std::mutex::try_lock — Попытка захвата мьютекса без блокировки потока

Atomic::load — Атомарное чтение значения из общей переменной

Atomic::store — Атомарная запись значения в общую переменную

Atomic::fetch_add — Атомарное увеличение значения с возвратом предыдущего

Atomic::fetch_sub — Атомарное уменьшение значения с возвратом предыдущего

Fopen — Открытие файла для ведения логов

Fprintf — Форматированный вывод в файл

Fflush — Принудительная запись буфера файла на диск

Fclose — Закрытие файла логов

Srand — Инициализация генератора случайных чисел

Rand — Генерация псевдослучайных чисел для временных задержек

Std::setlocale — Установка русской локали для корректного вывода

Std::thread::join — Ожидание завершения работы потоков

Std::vector::emplace_back — Создание потоков в контейнере без копирования

Описание алгоритма

Программа моделирует классическую задачу синхронизации «Читатели-Писатели» с приоритетом писателей. Алгоритм работает следующим образом:

1. Инициализация: Создаются общие ресурсы: storage – атомарная переменная для хранения данных; writeMutex – мьютекс для исключительной записи; readLock – флаг блокировки чтения при активной записи; activeReaders – счётчик активных читателей.

2. Потоки-писатели: Перед записью захватывают writeMutex. Устанавливают readLock = true, чтобы новые читатели не начинали чтение. Ждут, пока все активные читатели завершат чтение (activeReaders == 0). Выполняют запись в storage. Снимают readLock и освобождают writeMutex.

3. Потоки-читатели: Проверяют, не установлен ли readLock. Если запись не идёт, увеличивают activeReaders и начинают чтение. После чтения уменьшают activeReaders.

4. Логирование: Все действия потоков записываются в консоль и в файл log.txt с временными метками.

Таблица

Таблица, построенная на основе полученной статистики.

Таблица 1. Описание первых нескольких шагов

Время

Роль

Сообщение

Шаг 1

Писатель 0

обратился к хранилищу...

Шаг 2

Писатель 0

получил доступ к хранилищу.

Шаг 3

Писатель 0

записал в хранилище сообщение 18467 и закончил работу.

Шаг 4

Читатель 0

обратился к хранилищу...

Шаг 5

Читатель 0

получил доступ к хранилищу.

Шаг 6

Писатель 1

обратился к хранилищу...

Шаг 7

Писатель 1

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

Шаг 8

Читатель 0

прочитал из хранилища 18467 и закончил работу.

Шаг 9

Писатель 1

получил доступ к хранилищу.

Шаг 9

Писатель 1

записал в хранилище сообщение 18468 и закончил работу.

В рамках последовательности временных отметок зафиксированы взаимодействия между участниками системы хранения данных. Сначала Писатель 0 обращается к хранилищу и получает доступ, после чего записывает в хранилище сообщение с идентификатором 18467 и завершает работу. Затем Читатель 0 обращается к хранилищу и получает доступ, последующим шагом Писатель 1 обращается к хранилищу и ожидает, пока активные читатели закончат работу. В более поздний момент Читатель 0 читает из хранилища сообщение 18467 и завершает работу. После этого Писатель 1 снова получает доступ к хранилищу, записывает сообщение 18468 и завершает операцию. Общая картина демонстрирует последовательность чтения и записи с синхронизацией между читателями и писателями, включая ожидание завершения текущих операций перед продолжением записи новыми сообщениями.

Программа

Текст программы:

Рисунок 1. Программный файл

Продолжение программы:

Рисунок 2. Программный файл

Выходной файл:

Рисунок 3. Выходной файл

Вывод

В ходе лабораторной работы было разработано многопоточное приложение, реализующее алгоритм синхронизации «Читатели-Писатели» с приоритетом записи. Использованы стандартные средства языка C++ для работы с потоками и синхронизации: std::thread, std::mutex, std::atomic. Логирование действий каждого потока позволяет наглядно отслеживать порядок доступа к общему ресурсу и корректность работы алгоритма.

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

Приложение

Полный текст программы:

#include <iostream>

#include <fstream>

#include <thread>

#include <locale>

#include <vector>

#include <mutex>

#include <condition_variable>

#include <atomic>

#include <chrono>

#include <cstdlib>

#include <ctime>

#include <windows.h>

using namespace std;

FILE* streamOut = nullptr; // лог-файл

mutex writeMutex; // взаимная блокировка писателей

bool readLock = false;

mutex readLockMutex;

atomic<int> activeReaders(0);

void printTimeStamp() {

SYSTEMTIME t;

GetLocalTime(&t);

printf("%02d:%02d:%02d.%03d", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);

if (streamOut) {

fprintf(streamOut, "%02d:%02d:%02d.%03d", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);

}

}

void logPrint(const std::string& msg) {

static std::mutex logMutex;

std::lock_guard<std::mutex> lg(logMutex);

printTimeStamp();

printf("%s\n", msg.c_str());

if (streamOut) {

fprintf(streamOut, "%s\n", msg.c_str());

fflush(streamOut);

}

}

void writer(int totalWriters, atomic<int>& storage) {

int writerNumber;

vector<thread> writers;

for (writerNumber = 0; writerNumber < totalWriters; ++writerNumber) {

writers.emplace_back([writerNumber, &storage]() {

bool shown = false;

while (true) {

Sleep(rand() * (writerNumber + 1) % 15000 + 3000);

{

std::lock_guard<std::mutex> lg(writeMutex);

std::string msg = " Писатель " + std::to_string(writerNumber) + " обратился к хранилищу...";

logPrint(msg);

}

bool acquired = false;

shown = false;

while (!acquired) {

if (writeMutex.try_lock()) {

acquired = true;

} else {

if (!shown) {

std::string msg = "Другой писатель обратился к хранилищу раньше. Писатель " + std::to_string(writerNumber) + " ожидает.";

logPrint(msg);

shown = true;

}

Sleep(100);

}

}

{

std::lock_guard<std::mutex> rl(readLockMutex);

readLock = true;

}

shown = false;

while (activeReaders.load() != 0) {

if (!shown) {

std::string msg = "Писатель " + std::to_string(writerNumber) + " ожидает, пока активные читатели закончат работу с хранилищем.";

logPrint(msg);

shown = true;

}

Sleep(100);

}

printTimeStamp();

printf(": Писатель %d получил доступ к хранилищу.\n", writerNumber);

if (streamOut) fprintf(streamOut, ": Писатель %d получил доступ к хранилищу.\n", writerNumber);

Sleep(3000);

storage.store(rand() + writerNumber);

printTimeStamp();

printf(": Писатель %d записал в хранилище сообщение %d и закончил работу.\n", writerNumber, storage.load());

if (streamOut) fprintf(streamOut, ": Писатель %d записал в хранилище сообщение %d и закончил работу.\n", writerNumber, storage.load());

{

std::lock_guard<std::mutex> rl(readLockMutex);

readLock = false;

}

writeMutex.unlock();

}

});

}

for (auto& w : writers) w.join();

}

void reader(int totalReaders, atomic<int>& storage) {

vector<thread> readers;

for (int i = 0; i < totalReaders; ++i) {

readers.emplace_back([i, &storage]() {

bool reported = false;

while (true) {

Sleep(rand() * (i + 1) % 12000 + 3000);

{

std::lock_guard<std::mutex> lg(writeMutex);

printTimeStamp();

printf(": Читатель %d обратился к хранилищу...\n", i);

if (streamOut) fprintf(streamOut, ": Читатель %d обратился к хранилищу...\n", i);

}

while (true) {

bool locked;

{

std::lock_guard<std::mutex> rl(readLockMutex);

locked = readLock;

}

if (!locked) break;

if (!reported) {

std::string msg = "Доступ к хранилищу заблокирован. Читатель " + std::to_string(i) + " ожидает.";

logPrint(msg);

reported = true;

}

Sleep(100);

}

printTimeStamp();

printf(": Читатель %d получил доступ к хранилищу.\n", i);

if (streamOut) fprintf(streamOut, ": Читатель %d получил доступ к хранилищу.\n", i);

activeReaders.fetch_add(1);

Sleep(10000);

printTimeStamp();

printf(": Читатель %d прочитал из хранилища %d и закончил работу.\n", i, storage.load());

if (streamOut) fprintf(streamOut, ": Читатель %d прочитал из хранилища %d и закончил работу.\n", i, storage.load());

activeReaders.fetch_sub(1);

}

});

}

for (auto& r : readers) r.join();

}

int main() {

std::setlocale(LC_ALL, "Russian");

srand((unsigned)time(nullptr));

streamOut = fopen("log.txt", "w");

if (!streamOut) {

cerr << "Не удалось открыть log.txt" << endl;

return 1;

}

atomic<int> storage{0};

int totalWriters = 3;

int totalReaders = 5;

thread w(writer, totalWriters, std::ref(storage));

thread r(reader, totalReaders, std::ref(storage));

w.join();

r.join();

if (streamOut) fclose(streamOut);

return 0;

}