OS lb-8
This commit is contained in:
7
semester-4/ОС/lb-8/README.md
Normal file
7
semester-4/ОС/lb-8/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
> [!NOTE]
|
||||||
|
> Викладач: Мельникова Р. В.
|
||||||
|
>
|
||||||
|
> Оцінка: in progress
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Виконано для Linux в команді.
|
21
semester-4/ОС/lb-8/doc.yaml
Normal file
21
semester-4/ОС/lb-8/doc.yaml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
title: Керування потоками одного та декількох процесів
|
||||||
|
subject: ОС
|
||||||
|
doctype: ЛБ
|
||||||
|
worknumber: 8
|
||||||
|
mentors:
|
||||||
|
- name: Мельнікова Р. В.,
|
||||||
|
gender: f,
|
||||||
|
degree: доц. каф. ПІ,
|
||||||
|
edu_program: &EDU ПЗПІ
|
||||||
|
university: ХНУРЕ
|
||||||
|
authors:
|
||||||
|
- name: Ситник Є. С.
|
||||||
|
course: 2
|
||||||
|
edu: *EDU
|
||||||
|
gender: m
|
||||||
|
group: 23-2
|
||||||
|
- name: Малишкін. А. С.
|
||||||
|
course: 2
|
||||||
|
edu: *EDU
|
||||||
|
gender: m
|
||||||
|
group: 23-2
|
BIN
semester-4/ОС/lb-8/img/task-1-result.png
Normal file
BIN
semester-4/ОС/lb-8/img/task-1-result.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 180 KiB |
BIN
semester-4/ОС/lb-8/img/task-2-result.png
Normal file
BIN
semester-4/ОС/lb-8/img/task-2-result.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 161 KiB |
BIN
semester-4/ОС/lb-8/img/task-3-result.png
Normal file
BIN
semester-4/ОС/lb-8/img/task-3-result.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 189 KiB |
BIN
semester-4/ОС/lb-8/img/task-4-result.png
Normal file
BIN
semester-4/ОС/lb-8/img/task-4-result.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
17
semester-4/ОС/lb-8/src/.clangd
Normal file
17
semester-4/ОС/lb-8/src/.clangd
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
CompileFlags:
|
||||||
|
Add: [-Wall, -Wextra, -std=c++23, -DBUILD_SHARED]
|
||||||
|
CompilationDatabase: build/
|
||||||
|
|
||||||
|
Diagnostics:
|
||||||
|
UnusedIncludes: Strict
|
||||||
|
|
||||||
|
InlayHints:
|
||||||
|
Enabled: Yes
|
||||||
|
ParameterNames: Yes
|
||||||
|
DeducedTypes: Yes
|
||||||
|
|
||||||
|
Index:
|
||||||
|
Background: Build
|
||||||
|
|
||||||
|
Hover:
|
||||||
|
ShowAKA: Yes
|
73
semester-4/ОС/lb-8/src/.vscode/settings.json
vendored
Normal file
73
semester-4/ОС/lb-8/src/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"any": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"bit": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"charconv": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"condition_variable": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"list": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"format": "cpp",
|
||||||
|
"fstream": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"mutex": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"numbers": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"print": "cpp",
|
||||||
|
"queue": "cpp",
|
||||||
|
"ranges": "cpp",
|
||||||
|
"semaphore": "cpp",
|
||||||
|
"span": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"stop_token": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"text_encoding": "cpp",
|
||||||
|
"thread": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"typeinfo": "cpp",
|
||||||
|
"variant": "cpp",
|
||||||
|
"shared_mutex": "cpp"
|
||||||
|
}
|
||||||
|
}
|
28
semester-4/ОС/lb-8/src/.vscode/tasks.json
vendored
Normal file
28
semester-4/ОС/lb-8/src/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "cppbuild",
|
||||||
|
"label": "C/C++: g++ build active file",
|
||||||
|
"command": "/usr/bin/g++",
|
||||||
|
"args": [
|
||||||
|
"-fdiagnostics-color=always",
|
||||||
|
"-g",
|
||||||
|
"${file}",
|
||||||
|
"-o",
|
||||||
|
"${fileDirname}/${fileBasenameNoExtension}"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${fileDirname}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"detail": "Task generated by Debugger."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
35
semester-4/ОС/lb-8/src/Makefile
Normal file
35
semester-4/ОС/lb-8/src/Makefile
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
CXX = clang++
|
||||||
|
CXXFLAGS = -std=c++23 -Wall -Wextra -O2
|
||||||
|
|
||||||
|
task-1/build/app: task-1/main.cpp task-1/queue.hpp task-1/workers.hpp
|
||||||
|
@$(CXX) $(CXXFLAGS) task-1/main.cpp -o task-1/build/app
|
||||||
|
|
||||||
|
task-1: task-1/build/app
|
||||||
|
@./task-1/build/app
|
||||||
|
|
||||||
|
task-2/build/app: task-2/main.cpp task-2/vector.hpp task-2/workers.hpp
|
||||||
|
@$(CXX) $(CXXFLAGS) task-2/main.cpp -o task-2/build/app
|
||||||
|
|
||||||
|
task-2: task-2/build/app
|
||||||
|
@./task-2/build/app
|
||||||
|
|
||||||
|
task-3/build/app: task-3/main.cpp task-3/forks.hpp task-3/workers.hpp
|
||||||
|
@$(CXX) $(CXXFLAGS) task-3/main.cpp -o task-3/build/app
|
||||||
|
|
||||||
|
task-3: task-3/build/app
|
||||||
|
@./task-3/build/app
|
||||||
|
|
||||||
|
launcher/build/app: launcher/main.cpp launcher/queue.hpp launcher/workers.hpp
|
||||||
|
@$(CXX) $(CXXFLAGS) launcher/main.cpp -o launcher/build/app
|
||||||
|
|
||||||
|
launcher/build/launcher: launcher/launcher.cpp
|
||||||
|
@$(CXX) $(CXXFLAGS) launcher/launcher.cpp -o launcher/build/launcher
|
||||||
|
|
||||||
|
launcher: launcher/build/app launcher/build/launcher
|
||||||
|
@./launcher/build/launcher
|
||||||
|
|
||||||
|
launcher-app: launcher/build/app
|
||||||
|
@./launcher/build/app
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@find . -mindepth 2 -type d -name build -exec find {} -type f -delete \;
|
1
semester-4/ОС/lb-8/src/launcher/files/file1.txt
Normal file
1
semester-4/ОС/lb-8/src/launcher/files/file1.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello from ./launcher/files/file1.txt
|
1
semester-4/ОС/lb-8/src/launcher/files/file2.txt
Normal file
1
semester-4/ОС/lb-8/src/launcher/files/file2.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello from ./launcher/files/file2.txt
|
1
semester-4/ОС/lb-8/src/launcher/files/file3.txt
Normal file
1
semester-4/ОС/lb-8/src/launcher/files/file3.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello from ./launcher/files/file3.txt
|
23
semester-4/ОС/lb-8/src/launcher/launcher.cpp
Normal file
23
semester-4/ОС/lb-8/src/launcher/launcher.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include <chrono>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <print>
|
||||||
|
#include <ranges>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
constexpr std::int32_t runs = 3;
|
||||||
|
constexpr auto interval = std::chrono::seconds();
|
||||||
|
|
||||||
|
for (std::int32_t i : std::views::iota(0, runs)) {
|
||||||
|
std::print("\nLaunching: ./launcher/build/app (run {})\n", i + 1);
|
||||||
|
|
||||||
|
std::int32_t result = std::system("./launcher/build/app");
|
||||||
|
if (result != 0) {
|
||||||
|
std::print("Run {} failed with code {}\n", i + 1, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
40
semester-4/ОС/lb-8/src/launcher/main.cpp
Normal file
40
semester-4/ОС/lb-8/src/launcher/main.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include "queue.hpp"
|
||||||
|
#include "workers.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <ranges>
|
||||||
|
#include <format>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string prefix = "./launcher/files";
|
||||||
|
ipc::queue queue("/my_shared_queue");
|
||||||
|
|
||||||
|
std::vector<std::string> file_list = {
|
||||||
|
prefix + "/file1.txt",
|
||||||
|
prefix + "/file2.txt",
|
||||||
|
prefix + "/file3.txt"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto&& name : file_list) {
|
||||||
|
std::ofstream(name) << std::format("Hello from {}\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::jthread> producers;
|
||||||
|
for (int _ : std::views::iota(0, 2)) {
|
||||||
|
producers.emplace_back(workers::producer, std::ref(queue), std::ref(file_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::jthread> consumers;
|
||||||
|
for (int _ : std::views::iota(0, 4)) {
|
||||||
|
consumers.emplace_back(workers::consumer, std::ref(queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
producers.clear();
|
||||||
|
|
||||||
|
for (int _ : std::views::iota(0, 4)) {
|
||||||
|
queue.push("__DONE__");
|
||||||
|
}
|
||||||
|
}
|
93
semester-4/ОС/lb-8/src/launcher/queue.hpp
Normal file
93
semester-4/ОС/lb-8/src/launcher/queue.hpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
constexpr size_t MAX_PATH_LEN = 256;
|
||||||
|
constexpr size_t QUEUE_CAPACITY = 128;
|
||||||
|
|
||||||
|
struct shared_queue {
|
||||||
|
sem_t mutex;
|
||||||
|
sem_t slots;
|
||||||
|
sem_t items;
|
||||||
|
size_t head;
|
||||||
|
size_t tail;
|
||||||
|
char data[QUEUE_CAPACITY][MAX_PATH_LEN];
|
||||||
|
bool initialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
class queue {
|
||||||
|
public:
|
||||||
|
queue(const char *shm_name) {
|
||||||
|
shm_fd_ = ::shm_open(shm_name, O_CREAT | O_RDWR, 0666);
|
||||||
|
if (shm_fd_ < 0)
|
||||||
|
throw std::runtime_error("shm_open failed");
|
||||||
|
|
||||||
|
if (::ftruncate(shm_fd_, sizeof(shared_queue)) < 0)
|
||||||
|
throw std::runtime_error("ftruncate failed");
|
||||||
|
|
||||||
|
ptr_ = static_cast<shared_queue *>(::mmap(nullptr, sizeof(shared_queue),
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, shm_fd_, 0));
|
||||||
|
if (ptr_ == MAP_FAILED)
|
||||||
|
throw std::runtime_error("mmap failed");
|
||||||
|
|
||||||
|
if (!ptr_->initialized) {
|
||||||
|
initialize();
|
||||||
|
ptr_->initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~queue() {
|
||||||
|
::munmap(ptr_, sizeof(shared_queue));
|
||||||
|
::close(shm_fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(const std::string &s) {
|
||||||
|
if (s.size() >= MAX_PATH_LEN)
|
||||||
|
throw std::length_error("path too long");
|
||||||
|
::sem_wait(&ptr_->slots);
|
||||||
|
::sem_wait(&ptr_->mutex);
|
||||||
|
|
||||||
|
std::strncpy(ptr_->data[ptr_->tail], s.c_str(), MAX_PATH_LEN);
|
||||||
|
ptr_->tail = (ptr_->tail + 1) % QUEUE_CAPACITY;
|
||||||
|
|
||||||
|
::sem_post(&ptr_->mutex);
|
||||||
|
::sem_post(&ptr_->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pop(std::string &out) {
|
||||||
|
::sem_wait(&ptr_->items);
|
||||||
|
::sem_wait(&ptr_->mutex);
|
||||||
|
|
||||||
|
char buf[MAX_PATH_LEN];
|
||||||
|
std::strncpy(buf, ptr_->data[ptr_->head], MAX_PATH_LEN);
|
||||||
|
ptr_->head = (ptr_->head + 1) % QUEUE_CAPACITY;
|
||||||
|
|
||||||
|
::sem_post(&ptr_->mutex);
|
||||||
|
::sem_post(&ptr_->slots);
|
||||||
|
|
||||||
|
out = buf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int shm_fd_;
|
||||||
|
shared_queue *ptr_;
|
||||||
|
|
||||||
|
void initialize() {
|
||||||
|
::sem_init(&ptr_->mutex, 1, 1);
|
||||||
|
::sem_init(&ptr_->slots, 1, QUEUE_CAPACITY);
|
||||||
|
::sem_init(&ptr_->items, 1, 0);
|
||||||
|
ptr_->head = ptr_->tail = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace ipc
|
34
semester-4/ОС/lb-8/src/launcher/workers.hpp
Normal file
34
semester-4/ОС/lb-8/src/launcher/workers.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "queue.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <print>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace workers {
|
||||||
|
void producer(ipc::queue& q, const std::vector<std::string>& files) {
|
||||||
|
for (const auto& file : files) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
q.push(file);
|
||||||
|
std::print("Produced: {}\n", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void consumer(ipc::queue& queue) {
|
||||||
|
while (true) {
|
||||||
|
std::string path;
|
||||||
|
if (!queue.pop(path)) continue;
|
||||||
|
|
||||||
|
if (path == "__DONE__") break;
|
||||||
|
|
||||||
|
std::ifstream file(path);
|
||||||
|
std::print("Consumed: {}\n", path);
|
||||||
|
for (std::string line; std::getline(file, line);) {
|
||||||
|
std::print("\t{}\n", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
semester-4/ОС/lb-8/src/task-1/files/file1.txt
Normal file
1
semester-4/ОС/lb-8/src/task-1/files/file1.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello from ./task-1/files/file1.txt
|
1
semester-4/ОС/lb-8/src/task-1/files/file2.txt
Normal file
1
semester-4/ОС/lb-8/src/task-1/files/file2.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello from ./task-1/files/file2.txt
|
1
semester-4/ОС/lb-8/src/task-1/files/file3.txt
Normal file
1
semester-4/ОС/lb-8/src/task-1/files/file3.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello from ./task-1/files/file3.txt
|
36
semester-4/ОС/lb-8/src/task-1/main.cpp
Normal file
36
semester-4/ОС/lb-8/src/task-1/main.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include "queue.hpp"
|
||||||
|
#include "workers.hpp"
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <ranges>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string prefix = "./task-1/files";
|
||||||
|
|
||||||
|
th_safe::queue<std::string> queue;
|
||||||
|
|
||||||
|
std::vector<std::string> file_list = {
|
||||||
|
prefix + "/file1.txt", prefix + "/file2.txt", prefix + "/file3.txt"};
|
||||||
|
|
||||||
|
for (auto &&name : file_list) {
|
||||||
|
std::ofstream(name) << std::format("Hello from {}\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::jthread> producers;
|
||||||
|
for (std::int32_t i : std::views::iota(0, 2)) {
|
||||||
|
producers.emplace_back(workers::producer, std::ref(queue),
|
||||||
|
std::ref(file_list), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::jthread> consumers;
|
||||||
|
for (std::int32_t i : std::views::iota(0, 4)) {
|
||||||
|
consumers.emplace_back(workers::consumer, std::ref(queue), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
producers.clear();
|
||||||
|
queue.done();
|
||||||
|
}
|
45
semester-4/ОС/lb-8/src/task-1/queue.hpp
Normal file
45
semester-4/ОС/lb-8/src/task-1/queue.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
namespace th_safe {
|
||||||
|
template <typename T> class queue {
|
||||||
|
public:
|
||||||
|
void push(const T &value) {
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
queue_.push(value);
|
||||||
|
}
|
||||||
|
cond_var_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<T> pop() {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
cond_var_.wait(lock, [this] { return !queue_.empty() || done_; });
|
||||||
|
|
||||||
|
if (queue_.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
T value = queue_.front();
|
||||||
|
queue_.pop();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void done() {
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
done_ = true;
|
||||||
|
}
|
||||||
|
cond_var_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::queue<T> queue_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable cond_var_;
|
||||||
|
bool done_ = false;
|
||||||
|
};
|
||||||
|
} // namespace th_safe
|
40
semester-4/ОС/lb-8/src/task-1/workers.hpp
Normal file
40
semester-4/ОС/lb-8/src/task-1/workers.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "queue.hpp"
|
||||||
|
#include <chrono>
|
||||||
|
#include <fstream>
|
||||||
|
#include <print>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace workers {
|
||||||
|
void producer(th_safe::queue<std::string> &q,
|
||||||
|
const std::vector<std::string> &files, int id) {
|
||||||
|
for (const auto &file : files) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
q.push(file);
|
||||||
|
std::print("Produced ({}): {}\n", id, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void consumer(th_safe::queue<std::string> &q, int id) {
|
||||||
|
while (true) {
|
||||||
|
auto item = q.pop();
|
||||||
|
if (!item.has_value())
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::ifstream file(item.value());
|
||||||
|
if (!file) {
|
||||||
|
std::print("Failed to open: {}\n", item.value());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::print("Consumed ({}): {}\n", id, item.value());
|
||||||
|
for (std::string line; std::getline(file, line);) {
|
||||||
|
std::print("\t{}\n", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace workers
|
22
semester-4/ОС/lb-8/src/task-2/main.cpp
Normal file
22
semester-4/ОС/lb-8/src/task-2/main.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "vector.hpp"
|
||||||
|
#include "workers.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <ranges>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
th_safe::vector<std::string> news;
|
||||||
|
std::atomic_bool done = false;
|
||||||
|
|
||||||
|
std::jthread writer_thread([&] {
|
||||||
|
workers::writer(news, 10);
|
||||||
|
done = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<std::jthread> readers;
|
||||||
|
for (std::int32_t id : std::views::iota(1, 4)) {
|
||||||
|
readers.emplace_back(workers::reader, std::cref(news), std::ref(done), id);
|
||||||
|
}
|
||||||
|
}
|
32
semester-4/ОС/lb-8/src/task-2/vector.hpp
Normal file
32
semester-4/ОС/lb-8/src/task-2/vector.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace th_safe {
|
||||||
|
template<typename T>
|
||||||
|
class vector {
|
||||||
|
public:
|
||||||
|
void push_back(const T& value) {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
data_.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<T> back() const {
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
if (data_.empty()) return std::nullopt;
|
||||||
|
return data_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const {
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
return data_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::shared_mutex mutex_;
|
||||||
|
std::vector<T> data_;
|
||||||
|
};
|
||||||
|
}
|
47
semester-4/ОС/lb-8/src/task-2/workers.hpp
Normal file
47
semester-4/ОС/lb-8/src/task-2/workers.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "vector.hpp"
|
||||||
|
#include <chrono>
|
||||||
|
#include <print>
|
||||||
|
#include <ranges>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace workers {
|
||||||
|
void writer(th_safe::vector<std::string> &news_vector,
|
||||||
|
std::int32_t count = 10) {
|
||||||
|
for (std::int32_t i : std::views::iota(0, count)) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
|
||||||
|
auto news = std::format("News item {}", i + 1);
|
||||||
|
news_vector.push_back(news);
|
||||||
|
std::print("Writer: added '{}'\n", news);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reader(const th_safe::vector<std::string> &news_vector,
|
||||||
|
std::atomic_bool &done, std::int32_t id) {
|
||||||
|
std::size_t prev_size = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
auto last = news_vector.back();
|
||||||
|
|
||||||
|
if (!last.has_value()) {
|
||||||
|
std::print("Reader {}: no news yet\n", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t current_size = news_vector.size();
|
||||||
|
if (current_size == prev_size && !done)
|
||||||
|
continue;
|
||||||
|
if (current_size == prev_size && done)
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::print("Reader {}: reads '{}'\n", id, *last);
|
||||||
|
prev_size = current_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::print("Reader {}: finished.\n", id);
|
||||||
|
}
|
||||||
|
} // namespace workers
|
22
semester-4/ОС/lb-8/src/task-3/forks.hpp
Normal file
22
semester-4/ОС/lb-8/src/task-3/forks.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace th_safe {
|
||||||
|
class forks {
|
||||||
|
public:
|
||||||
|
explicit forks(std::size_t count) : mutexes_(count) {}
|
||||||
|
|
||||||
|
std::mutex &left(std::size_t i) { return mutexes_[i]; }
|
||||||
|
|
||||||
|
std::mutex &right(std::size_t i) {
|
||||||
|
return mutexes_[(i + 1) % mutexes_.size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const { return mutexes_.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::mutex> mutexes_;
|
||||||
|
};
|
||||||
|
} // namespace th_safe
|
20
semester-4/ОС/lb-8/src/task-3/main.cpp
Normal file
20
semester-4/ОС/lb-8/src/task-3/main.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "forks.hpp"
|
||||||
|
#include "workers.hpp"
|
||||||
|
|
||||||
|
#include <print>
|
||||||
|
#include <ranges>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const std::size_t num_philosophers = 4;
|
||||||
|
const std::size_t rounds = 3;
|
||||||
|
|
||||||
|
th_safe::forks table(num_philosophers);
|
||||||
|
|
||||||
|
std::vector<std::jthread> philosophers;
|
||||||
|
for (auto id : std::views::iota(0UL, num_philosophers)) {
|
||||||
|
philosophers.emplace_back(workers::philosopher, id, std::ref(table),
|
||||||
|
rounds);
|
||||||
|
}
|
||||||
|
}
|
43
semester-4/ОС/lb-8/src/task-3/workers.hpp
Normal file
43
semester-4/ОС/lb-8/src/task-3/workers.hpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "forks.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
|
#include <print>
|
||||||
|
#include <random>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace workers {
|
||||||
|
void philosopher(std::size_t id, th_safe::forks &table,
|
||||||
|
std::size_t iterations = 3) {
|
||||||
|
std::mt19937 rng(id + std::random_device{}());
|
||||||
|
std::uniform_int_distribution<> think_time(100, 300);
|
||||||
|
std::uniform_int_distribution<> eat_time(100, 200);
|
||||||
|
|
||||||
|
for (std::size_t round = 0; round < iterations; ++round) {
|
||||||
|
std::print("Philosopher {} is thinking (round {})\n", id, round + 1);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(think_time(rng)));
|
||||||
|
|
||||||
|
std::mutex &left_fork = table.left(id);
|
||||||
|
std::mutex &right_fork = table.right(id);
|
||||||
|
|
||||||
|
if (std::addressof(left_fork) < std::addressof(right_fork)) {
|
||||||
|
std::scoped_lock lock(left_fork, right_fork);
|
||||||
|
std::print("Philosopher {} is eating (round {})\n", id, round + 1);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(eat_time(rng)));
|
||||||
|
std::print("Philosopher {} finished eating (round {})\n", id, round + 1);
|
||||||
|
} else {
|
||||||
|
std::scoped_lock lock(right_fork, left_fork);
|
||||||
|
std::print("Philosopher {} is eating (round {})\n", id, round + 1);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(eat_time(rng)));
|
||||||
|
std::print("Philosopher {} finished eating (round {})\n", id, round + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::print("Philosopher {} leaves the table.\n", id);
|
||||||
|
}
|
||||||
|
} // namespace workers
|
BIN
semester-4/ОС/lb-8/Лр_8_Ситник_Малишкін_ПЗПІ_23_2.pdf
Normal file
BIN
semester-4/ОС/lb-8/Лр_8_Ситник_Малишкін_ПЗПІ_23_2.pdf
Normal file
Binary file not shown.
489
semester-4/ОС/lb-8/Лр_8_Ситник_Малишкін_ПЗПІ_23_2.typ
Normal file
489
semester-4/ОС/lb-8/Лр_8_Ситник_Малишкін_ПЗПІ_23_2.typ
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
#import "@local/nure:0.1.0": *
|
||||||
|
|
||||||
|
#show: pz-lb.with(..yaml("doc.yaml"))
|
||||||
|
|
||||||
|
#v(-spacing)
|
||||||
|
|
||||||
|
== Мета роботи
|
||||||
|
Метою даної лабораторної роботи є вивчення створення процесів та потоків при виконанні
|
||||||
|
програм.
|
||||||
|
|
||||||
|
== Хід роботи
|
||||||
|
Оскільки ми використовуємо операційну систему Linux, дану лабораторну роботу нами було виконано із використанням крос-платформних функцій стандартної бібліотеки C++, за виключенням останнього завдання, для якого було задіяно засоби POSIX API, адже стандартна бібліотека не надає засобів синхронізації процесів.
|
||||||
|
|
||||||
|
=== Задача №1
|
||||||
|
Мета даного завдання -- розробити програму, в якій декілька "виробників" (потоків) будуть знаходити імена файлів і додавати їх до спільної "черги". Одночасно декілька "споживачів" (інших потоків) братимуть ці імена файлів з черги і виводитимуть вміст кожного файлу.
|
||||||
|
|
||||||
|
Для перевірки роботи програми використаємо 2 потоки виробника та 4 потоки споживача, які будуть обробляти 3 файли.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int main() {
|
||||||
|
std::string prefix = "./task-1/files";
|
||||||
|
|
||||||
|
th_safe::queue<std::string> queue;
|
||||||
|
|
||||||
|
std::vector<std::string> file_list = {
|
||||||
|
prefix + "/file1.txt",
|
||||||
|
prefix + "/file2.txt",
|
||||||
|
prefix + "/file3.txt"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &&name : file_list) {
|
||||||
|
std::ofstream(name) << std::format("Hello from {}\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::jthread> producers;
|
||||||
|
for (std::int32_t i : std::views::iota(0, 2)) {
|
||||||
|
producers.emplace_back(
|
||||||
|
workers::producer,
|
||||||
|
std::ref(queue),
|
||||||
|
std::ref(file_list),
|
||||||
|
i
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::jthread> consumers;
|
||||||
|
for (std::int32_t i : std::views::iota(0, 4)) {
|
||||||
|
consumers.emplace_back(
|
||||||
|
workers::consumer,
|
||||||
|
std::ref(queue),
|
||||||
|
i
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
producers.clear();
|
||||||
|
queue.done();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Для зручнішої роботи із чергою з стандартної бібліотеки в багато-потоковому контексті створимо клас обгортку, що реалізує методи взаємодії із чергою з використанням м'ютексів.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace th_safe {
|
||||||
|
template <typename T> class queue {
|
||||||
|
public:
|
||||||
|
void push(const T &value) {
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
queue_.push(value);
|
||||||
|
}
|
||||||
|
cond_var_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<T> pop() {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
cond_var_.wait(
|
||||||
|
lock,
|
||||||
|
[this] { return !queue_.empty() || done_; }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (queue_.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
T value = queue_.front();
|
||||||
|
queue_.pop();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void done() {
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
done_ = true;
|
||||||
|
}
|
||||||
|
cond_var_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::queue<T> queue_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable cond_var_;
|
||||||
|
bool done_ = false;
|
||||||
|
};
|
||||||
|
} // namespace th_safe
|
||||||
|
```
|
||||||
|
|
||||||
|
Виробники додають файли в чергу, а споживачі зчитують їхній вміст.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace workers {
|
||||||
|
void producer(
|
||||||
|
th_safe::queue<std::string> &q,
|
||||||
|
const std::vector<std::string> &files,
|
||||||
|
int id
|
||||||
|
) {
|
||||||
|
for (const auto &file : files) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
q.push(file);
|
||||||
|
std::print("Produced ({}): {}\n", id, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void consumer(th_safe::queue<std::string> &q, int id) {
|
||||||
|
while (true) {
|
||||||
|
auto item = q.pop();
|
||||||
|
if (!item.has_value())
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::ifstream file(item.value());
|
||||||
|
if (!file) {
|
||||||
|
std::print("Failed to open: {}\n", item.value());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::print("Consumed ({}): {}\n", id, item.value());
|
||||||
|
for (std::string line; std::getline(file, line);) {
|
||||||
|
std::print("\t{}\n", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace workers
|
||||||
|
```
|
||||||
|
|
||||||
|
Перевіримо правильність роботи програми
|
||||||
|
#figure(image("img/task-1-result.png", width: 70%), caption: [Результат виконання програми])
|
||||||
|
|
||||||
|
=== Задача №2
|
||||||
|
Мета даного завдання -- створити програму з потоками "читачів" та "письменників". Потоки-письменники будуть додавати нові записи в кінець спільного списку новин. Паралельно потоки-читачі будуть отримувати та читати останню додану новину з цього ж списку.
|
||||||
|
|
||||||
|
Для перевірки роботи програми використаємо 1 потік письменник та 3 потоки читачі.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int main() {
|
||||||
|
th_safe::vector<std::string> news;
|
||||||
|
std::atomic_bool done = false;
|
||||||
|
|
||||||
|
std::jthread writer_thread([&] {
|
||||||
|
workers::writer(news, 10);
|
||||||
|
done = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<std::jthread> readers;
|
||||||
|
for (std::int32_t id : std::views::iota(1, 4)) {
|
||||||
|
readers.emplace_back(workers::reader, std::cref(news), std::ref(done), id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Для зручнішої роботи із вектором з стандартної бібліотеки в багато-потоковому контексті створимо клас обгортку, що реалізує методи взаємодії із вектором з використанням м'ютексів.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace th_safe {
|
||||||
|
template<typename T>
|
||||||
|
class vector {
|
||||||
|
public:
|
||||||
|
void push_back(const T& value) {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
data_.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<T> back() const {
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
if (data_.empty()) return std::nullopt;
|
||||||
|
return data_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const {
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
return data_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::shared_mutex mutex_;
|
||||||
|
std::vector<T> data_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Письменник додає новини в вектор, а читачі зчитують з вектора останню додану новину.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace workers {
|
||||||
|
void writer(
|
||||||
|
th_safe::vector<std::string> &news_vector,
|
||||||
|
std::int32_t count = 10
|
||||||
|
) {
|
||||||
|
for (std::int32_t i : std::views::iota(0, count)) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
|
||||||
|
auto news = std::format("News item {}", i + 1);
|
||||||
|
news_vector.push_back(news);
|
||||||
|
std::print("Writer: added '{}'\n", news);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reader(
|
||||||
|
const th_safe::vector<std::string> &news_vector,
|
||||||
|
std::atomic_bool &done,
|
||||||
|
std::int32_t id
|
||||||
|
) {
|
||||||
|
std::size_t prev_size = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
auto last = news_vector.back();
|
||||||
|
|
||||||
|
if (!last.has_value()) {
|
||||||
|
std::print("Reader {}: no news yet\n", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t current_size = news_vector.size();
|
||||||
|
if (current_size == prev_size && !done)
|
||||||
|
continue;
|
||||||
|
if (current_size == prev_size && done)
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::print("Reader {}: reads '{}'\n", id, *last);
|
||||||
|
prev_size = current_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::print("Reader {}: finished.\n", id);
|
||||||
|
}
|
||||||
|
} // namespace workers
|
||||||
|
```
|
||||||
|
|
||||||
|
Перевіримо правильність роботи програми
|
||||||
|
#figure(image("img/task-2-result.png"), caption: [Результат виконання програми])
|
||||||
|
|
||||||
|
=== Задача №3
|
||||||
|
Мета даного завдання -- реалізувати програму, яка моделює відому проблему "обідаючих філософів" за допомогою потоків. Кожен потік представлятиме філософа, який проходить повний цикл дій: думає, бере по черзі дві виделки, обідає, а потім кладе виделки на місце. Цей цикл має повторюватися задану кількість разів для кожного філософа.
|
||||||
|
|
||||||
|
Для перевірки роботи програми використаємо 5 потоків філософів та 3 повторення.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int main() {
|
||||||
|
const std::size_t num_philosophers = 5;
|
||||||
|
const std::size_t rounds = 3;
|
||||||
|
|
||||||
|
th_safe::forks table(num_philosophers);
|
||||||
|
|
||||||
|
std::vector<std::jthread> philosophers;
|
||||||
|
for (auto id : std::views::iota(0UL, num_philosophers)) {
|
||||||
|
philosophers.emplace_back(
|
||||||
|
workers::philosopher,
|
||||||
|
id,
|
||||||
|
std::ref(table),
|
||||||
|
rounds
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Для зручної роботи із "столом" в багато-потоковому контексті створимо клас, що реалізує методи взаємодії із столом з використанням м'ютексів.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace th_safe {
|
||||||
|
class forks {
|
||||||
|
public:
|
||||||
|
explicit forks(std::size_t count) : mutexes_(count) {}
|
||||||
|
|
||||||
|
std::mutex &left(std::size_t i) { return mutexes_[i]; }
|
||||||
|
|
||||||
|
std::mutex &right(std::size_t i) {
|
||||||
|
return mutexes_[(i + 1) % mutexes_.size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const { return mutexes_.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::mutex> mutexes_;
|
||||||
|
};
|
||||||
|
} // namespace th_safe
|
||||||
|
```
|
||||||
|
|
||||||
|
Кожен з філософів по черзі із певною затримкою виконує одну із можливих дій.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace workers {
|
||||||
|
void philosopher(
|
||||||
|
std::size_t id,
|
||||||
|
th_safe::forks &table,
|
||||||
|
std::size_t iterations = 3
|
||||||
|
) {
|
||||||
|
std::mt19937 rng(id + std::random_device{}());
|
||||||
|
std::uniform_int_distribution<> think_time(100, 300);
|
||||||
|
std::uniform_int_distribution<> eat_time(100, 200);
|
||||||
|
|
||||||
|
for (std::size_t round = 0; round < iterations; ++round) {
|
||||||
|
std::print(
|
||||||
|
"Philosopher {} is thinking (round {})\n",
|
||||||
|
id,
|
||||||
|
round + 1
|
||||||
|
);
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(think_time(rng))
|
||||||
|
);
|
||||||
|
|
||||||
|
std::mutex &left_fork = table.left(id);
|
||||||
|
std::mutex &right_fork = table.right(id);
|
||||||
|
|
||||||
|
if (std::addressof(left_fork) < std::addressof(right_fork)) {
|
||||||
|
std::scoped_lock lock(left_fork, right_fork);
|
||||||
|
std::print(
|
||||||
|
"Philosopher {} is eating (round {})\n",
|
||||||
|
id,
|
||||||
|
round + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(eat_time(rng))
|
||||||
|
);
|
||||||
|
std::print(
|
||||||
|
"Philosopher {} finished eating (round {})\n",
|
||||||
|
id,
|
||||||
|
round + 1
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
std::scoped_lock lock(right_fork, left_fork);
|
||||||
|
std::print("Philosopher {
|
||||||
|
} is eating (round {})\n", id, round + 1);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(eat_time(rng))
|
||||||
|
);
|
||||||
|
std::print(
|
||||||
|
"Philosopher {} finished eating (round {})\n",
|
||||||
|
id,
|
||||||
|
round + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::print("Philosopher {} leaves the table.\n", id);
|
||||||
|
}
|
||||||
|
} // namespace workers
|
||||||
|
```
|
||||||
|
|
||||||
|
Перевіримо правильність роботи програми
|
||||||
|
#figure(image("img/task-3-result.png", height: 80%), caption: [Результат виконання програми])
|
||||||
|
|
||||||
|
=== Завдання на найвищу оцінку
|
||||||
|
Мета даного завдання -- створити окрему програму, яка буде багаторазово запускати одну з попередніх програм. Ці запуски повинні відбуватися за певним заданим розкладом, використовуючи механізм, такий як WaitableTimer.
|
||||||
|
|
||||||
|
Виконаємо завдання для програми №1.
|
||||||
|
|
||||||
|
Перевіримо роботу із 3 окремим процесами.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int main() {
|
||||||
|
constexpr std::int32_t runs = 3;
|
||||||
|
constexpr auto interval = std::chrono::seconds();
|
||||||
|
|
||||||
|
for (std::int32_t i : std::views::iota(0, runs)) {
|
||||||
|
std::print("\nLaunching: ./launcher/build/app (run {})\n", i + 1);
|
||||||
|
|
||||||
|
std::int32_t result = std::system("./launcher/build/app");
|
||||||
|
if (result != 0) {
|
||||||
|
std::print("Run {} failed with code {}\n", i + 1, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Щоб кілька окремих процесів мали доступ до однієї черги використаємо функцію "mmap". Для синхронізації кількох процесів скористаємося семафорами.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
constexpr size_t MAX_PATH_LEN = 256;
|
||||||
|
constexpr size_t QUEUE_CAPACITY = 128;
|
||||||
|
|
||||||
|
struct shared_queue {
|
||||||
|
sem_t mutex;
|
||||||
|
sem_t slots;
|
||||||
|
sem_t items;
|
||||||
|
size_t head;
|
||||||
|
size_t tail;
|
||||||
|
char data[QUEUE_CAPACITY][MAX_PATH_LEN];
|
||||||
|
bool initialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
class queue {
|
||||||
|
public:
|
||||||
|
queue(const char *shm_name) {
|
||||||
|
shm_fd_ = ::shm_open(shm_name, O_CREAT | O_RDWR, 0666);
|
||||||
|
if (shm_fd_ < 0)
|
||||||
|
throw std::runtime_error("shm_open failed");
|
||||||
|
|
||||||
|
if (::ftruncate(shm_fd_, sizeof(shared_queue)) < 0)
|
||||||
|
throw std::runtime_error("ftruncate failed");
|
||||||
|
|
||||||
|
ptr_ = static_cast<shared_queue *>(
|
||||||
|
::mmap(
|
||||||
|
nullptr,
|
||||||
|
sizeof(shared_queue),
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
shm_fd_,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (ptr_ == MAP_FAILED)
|
||||||
|
throw std::runtime_error("mmap failed");
|
||||||
|
|
||||||
|
if (!ptr_->initialized) {
|
||||||
|
initialize();
|
||||||
|
ptr_->initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~queue() {
|
||||||
|
::munmap(ptr_, sizeof(shared_queue));
|
||||||
|
::close(shm_fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(const std::string &s) {
|
||||||
|
if (s.size() >= MAX_PATH_LEN)
|
||||||
|
throw std::length_error("path too long");
|
||||||
|
::sem_wait(&ptr_->slots);
|
||||||
|
::sem_wait(&ptr_->mutex);
|
||||||
|
|
||||||
|
std::strncpy(ptr_->data[ptr_->tail], s.c_str(), MAX_PATH_LEN);
|
||||||
|
ptr_->tail = (ptr_->tail + 1) % QUEUE_CAPACITY;
|
||||||
|
|
||||||
|
::sem_post(&ptr_->mutex);
|
||||||
|
::sem_post(&ptr_->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pop(std::string &out) {
|
||||||
|
::sem_wait(&ptr_->items);
|
||||||
|
::sem_wait(&ptr_->mutex);
|
||||||
|
|
||||||
|
char buf[MAX_PATH_LEN];
|
||||||
|
std::strncpy(buf, ptr_->data[ptr_->head], MAX_PATH_LEN);
|
||||||
|
ptr_->head = (ptr_->head + 1) % QUEUE_CAPACITY;
|
||||||
|
|
||||||
|
::sem_post(&ptr_->mutex);
|
||||||
|
::sem_post(&ptr_->slots);
|
||||||
|
|
||||||
|
out = buf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int shm_fd_;
|
||||||
|
shared_queue *ptr_;
|
||||||
|
|
||||||
|
void initialize() {
|
||||||
|
::sem_init(&ptr_->mutex, 1, 1);
|
||||||
|
::sem_init(&ptr_->slots, 1, QUEUE_CAPACITY);
|
||||||
|
::sem_init(&ptr_->items, 1, 0);
|
||||||
|
ptr_->head = ptr_->tail = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace ipc
|
||||||
|
```
|
||||||
|
|
||||||
|
Перевіримо роботу зміненої програми.
|
||||||
|
|
||||||
|
#figure(image("img/task-4-result.png", height: 72%), caption: [Результат виконання програми])
|
||||||
|
|
||||||
|
== Висновки
|
||||||
|
Під час даної лабораторної роботи ми навчились використовувати процеси та потоки при виконанні програм.
|
||||||
|
|
||||||
|
#show: appendices_style
|
Reference in New Issue
Block a user