Программа
Текст программы:
#include <iostream>
#include <vector>
#include <thread>
#include <atomic>
#include <chrono>
#include <semaphore>
#include <mutex>
#include <condition_variable>
#include <fstream>
#include <locale>
#include <windows.h>
using namespace std;
using Clock = chrono::steady_clock;
struct TimeStats {
vector<long long> durationsUs;
void addDuration(long long us) { durationsUs.push_back(us); }
};
// Оптимизированный семафор без мьютексов, на атомиках и Windows event
class FastSemaphore {
private:
std::atomic<long> count;
HANDLE event;
public:
explicit FastSemaphore(long initial) : count(initial) {
event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!event) {
throw runtime_error("CreateEvent failed");
}
}
~FastSemaphore() {
CloseHandle(event);
}
void wait() {
while (true) {
long oldCount = count.load(std::memory_order_acquire);
if (oldCount > 0) {
if (count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire))
return; // успешно заняли место
} else {
// Перед ожиданием еще раз проверить, чтобы не пропустить сигнал
if (count.load(std::memory_order_acquire) > 0)
continue;
WaitForSingleObject(event, INFINITE);
}
}
}
void signal() {
long oldCount = count.fetch_add(1, std::memory_order_release);
if (oldCount <= 0) {
SetEvent(event); // пробуждаем поток
}
}
};
struct SharedStorage {
atomic<int> data{0};
};
// Поток-писатель с FastSemaphore
void writerFastFunc(SharedStorage& storage, FastSemaphore& sem, TimeStats& stats, int ops) {
for (int i = 0; i < ops; ++i) {
sem.wait();
auto start = Clock::now();
this_thread::sleep_for(chrono::milliseconds(10));
storage.data.store(rand() % 1000);
auto end = Clock::now();
sem.signal();
long long durUs = chrono::duration_cast<chrono::microseconds>(end - start).count();
stats.addDuration(durUs);
}
}
// Поток-читатель с FastSemaphore
void readerFastFunc(SharedStorage& storage, FastSemaphore& sem, TimeStats& stats, int ops) {
for (int i = 0; i < ops; ++i) {
sem.wait();
auto start = Clock::now();
this_thread::sleep_for(chrono::milliseconds(5));
volatile int val = storage.data.load();
auto end = Clock::now();
sem.signal();
long long durUs = chrono::duration_cast<chrono::microseconds>(end - start).count();
stats.addDuration(durUs);
}
}
// Поток-писатель с std::counting_semaphore
void writerStdFunc(SharedStorage& storage, std::counting_semaphore<1>& sem, TimeStats& stats, int ops) {
for (int i = 0; i < ops; ++i) {
sem.acquire();
auto start = Clock::now();
this_thread::sleep_for(chrono::milliseconds(10));
storage.data.store(rand() % 1000);
auto end = Clock::now();
sem.release();
long long durUs = chrono::duration_cast<chrono::microseconds>(end - start).count();
stats.addDuration(durUs);
}
}
// Поток-читатель с std::counting_semaphore
void readerStdFunc(SharedStorage& storage, std::counting_semaphore<1>& sem, TimeStats& stats, int ops) {
for (int i = 0; i < ops; ++i) {
sem.acquire();
auto start = Clock::now();
this_thread::sleep_for(chrono::milliseconds(5));
volatile int val = storage.data.load();
auto end = Clock::now();
sem.release();
long long durUs = chrono::duration_cast<chrono::microseconds>(end - start).count();
stats.addDuration(durUs);
}
}
int main() {
std::setlocale(LC_ALL, "Russian");
const int writersCount = 8;
const int readersCount = 8;
const int opsPerThread = 1000;
cout << "Запуск теста с FastSemaphore...\n";
SharedStorage storageFast;
FastSemaphore semaphoreFast(1);
vector<thread> threadsFast;
vector<TimeStats> writerStatsFast(writersCount);
vector<TimeStats> readerStatsFast(readersCount);
for (int i = 0; i < writersCount; ++i)
threadsFast.emplace_back(writerFastFunc, ref(storageFast), ref(semaphoreFast), ref(writerStatsFast[i]), opsPerThread);
for (int i = 0; i < readersCount; ++i)
threadsFast.emplace_back(readerFastFunc, ref(storageFast), ref(semaphoreFast), ref(readerStatsFast[i]), opsPerThread);
for (auto& t : threadsFast) t.join();
cout << "Запуск теста с std::counting_semaphore...\n";
SharedStorage storageStd;
std::counting_semaphore<1> semaphoreStd(1);
vector<thread> threadsStd;
vector<TimeStats> writerStatsStd(writersCount);
vector<TimeStats> readerStatsStd(readersCount);
for (int i = 0; i < writersCount; ++i)
threadsStd.emplace_back(writerStdFunc, ref(storageStd), ref(semaphoreStd), ref(writerStatsStd[i]), opsPerThread);
for (int i = 0; i < readersCount; ++i)
threadsStd.emplace_back(readerStdFunc, ref(storageStd), ref(semaphoreStd), ref(readerStatsStd[i]), opsPerThread);
for (auto& t : threadsStd) t.join();
// Сохраняем результаты в файлы для анализа
ofstream outFast("fast_semaphore_times.csv");
outFast << "ThreadType,ThreadID,Operation,DurationUs\n";
for (int i = 0; i < writersCount; ++i)
for (size_t j = 0; j < writerStatsFast[i].durationsUs.size(); ++j)
outFast << "Writer," << i << "," << j << "," << writerStatsFast[i].durationsUs[j] << "\n";
for (int i = 0; i < readersCount; ++i)
for (size_t j = 0; j < readerStatsFast[i].durationsUs.size(); ++j)
outFast << "Reader," << i << "," << j << "," << readerStatsFast[i].durationsUs[j] << "\n";
outFast.close();
ofstream outStd("std_semaphore_times.csv");
outStd << "ThreadType,ThreadID,Operation,DurationUs\n";
for (int i = 0; i < writersCount; ++i)
for (size_t j = 0; j < writerStatsStd[i].durationsUs.size(); ++j)
outStd << "Writer," << i << "," << j << "," << writerStatsStd[i].durationsUs[j] << "\n";
for (int i = 0; i < readersCount; ++i)
for (size_t j = 0; j < readerStatsStd[i].durationsUs.size(); ++j)
outStd << "Reader," << i << "," << j << "," << readerStatsStd[i].durationsUs[j] << "\n";
outStd.close();
cout << "Тесты завершены. Результаты сохранены в fast_semaphore_times.csv и std_semaphore_times.csv\n";
return 0;
}
Выходной файл:
Рисунок 2. Выходной файл
Вывод
В ходе лабораторной работы было разработано многопоточное приложение, реализующее алгоритм синхронизации «Читатели-Писатели» с приоритетом записи. Использованы стандартные средства языка C++ для работы с потоками и синхронизации.
