coloriot

HA_69

Sep 12th, 2025
295
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.23 KB | None | 0 0
  1. /*
  2.  * Reader-Writer Lock с предотвращением голодания
  3.  * Полная реализация в одном файле
  4.  */
  5.  
  6. #include <iostream>
  7. #include <thread>
  8. #include <mutex>
  9. #include <condition_variable>
  10. #include <queue>
  11. #include <chrono>
  12. #include <atomic>
  13. #include <vector>
  14. #include <iomanip>
  15.  
  16. // Типы запросов в очереди
  17. enum RequestType {
  18.     READER_REQUEST = 1,
  19.     WRITER_REQUEST = 2
  20. };
  21.  
  22. // Элемент очереди с билетом и типом запроса
  23. struct QueueItem {
  24.     int ticket;
  25.     RequestType type;
  26.  
  27.     QueueItem(int t, RequestType rt) : ticket(t), type(rt) {}
  28. };
  29.  
  30. // Основной класс RW Lock с предотвращением голодания
  31. class RW_Lock {
  32. private:
  33.     std::mutex mutex_;                           // Основной мютекс
  34.     std::condition_variable cv_;                 // Для блокировки потоков
  35.     std::queue<QueueItem> waiting_queue_;        // FIFO очередь запросов
  36.  
  37.     int active_readers_ = 0;                     // Счетчик активных читателей
  38.     bool active_writer_ = false;                 // Флаг активного писателя
  39.     int next_ticket_ = 0;                        // Следующий номер билета
  40.     int current_ticket_ = 0;                     // Текущий обслуживаемый билет
  41.  
  42. public:
  43.     // Захват блокировки для чтения
  44.     void read_lock() {
  45.         std::unique_lock<std::mutex> lock(mutex_);
  46.  
  47.         // Получаем уникальный билет
  48.         int my_ticket = next_ticket_++;
  49.  
  50.         // Добавляем себя в очередь
  51.         waiting_queue_.emplace(my_ticket, READER_REQUEST);
  52.  
  53.         // Ждем своей очереди
  54.         cv_.wait(lock, [this, my_ticket] {
  55.             return current_ticket_ == my_ticket;
  56.         });
  57.  
  58.         // Ждем пока нет активного писателя
  59.         cv_.wait(lock, [this] {
  60.             return !active_writer_;
  61.         });
  62.  
  63.         // Увеличиваем счетчик читателей
  64.         ++active_readers_;
  65.  
  66.         // Убираем себя из очереди и обрабатываем следующих
  67.         waiting_queue_.pop();
  68.         process_next_requests();
  69.     }
  70.  
  71.     // Освобождение блокировки чтения
  72.     void read_unlock() {
  73.         std::unique_lock<std::mutex> lock(mutex_);
  74.         --active_readers_;
  75.  
  76.         // Если последний читатель, уведомляем писателей
  77.         if (active_readers_ == 0) {
  78.             cv_.notify_all();
  79.         }
  80.     }
  81.  
  82.     // Захват блокировки для записи
  83.     void write_lock() {
  84.         std::unique_lock<std::mutex> lock(mutex_);
  85.  
  86.         // Получаем уникальный билет
  87.         int my_ticket = next_ticket_++;
  88.  
  89.         // Добавляем себя в очередь
  90.         waiting_queue_.emplace(my_ticket, WRITER_REQUEST);
  91.  
  92.         // Ждем своей очереди
  93.         cv_.wait(lock, [this, my_ticket] {
  94.             return current_ticket_ == my_ticket;
  95.         });
  96.  
  97.         // Ждем пока нет активных читателей и писателей
  98.         cv_.wait(lock, [this] {
  99.             return active_readers_ == 0 && !active_writer_;
  100.         });
  101.  
  102.         // Устанавливаем флаг писателя
  103.         active_writer_ = true;
  104.  
  105.         // Убираем себя из очереди
  106.         waiting_queue_.pop();
  107.         ++current_ticket_;
  108.     }
  109.  
  110.     // Освобождение блокировки записи
  111.     void write_unlock() {
  112.         std::unique_lock<std::mutex> lock(mutex_);
  113.         active_writer_ = false;
  114.         process_next_requests();
  115.         cv_.notify_all();
  116.     }
  117.  
  118.     // Получить статистику (для отладки)
  119.     void print_stats() {
  120.         std::unique_lock<std::mutex> lock(mutex_);
  121.         std::cout << "[DEBUG] Readers: " << active_readers_
  122.                   << ", Writer: " << (active_writer_ ? "YES" : "NO")
  123.                   << ", Queue size: " << waiting_queue_.size()
  124.                   << ", Current ticket: " << current_ticket_ << std::endl;
  125.     }
  126.  
  127. private:
  128.     // Обработка следующих запросов в очереди
  129.     void process_next_requests() {
  130.         if (waiting_queue_.empty()) {
  131.             ++current_ticket_;
  132.         } else {
  133.             ++current_ticket_;
  134.         }
  135.     }
  136. };
  137.  
  138. // Класс для тестирования RW Lock
  139. class RWLockTester {
  140. private:
  141.     RW_Lock rw_lock_;
  142.     std::atomic<int> shared_data_{0};
  143.     std::atomic<int> reader_ops_{0};
  144.     std::atomic<int> writer_ops_{0};
  145.     std::atomic<bool> test_running_{true};
  146.  
  147. public:
  148.     // Тест 1: Базовая функциональность
  149.     void test_basic_functionality() {
  150.         std::cout << "\n=== TEST 1 ===" << std::endl;
  151.  
  152.         shared_data_ = 0;
  153.         reader_ops_ = 0;
  154.         writer_ops_ = 0;
  155.  
  156.         const int num_readers = 3;
  157.         const int num_writers = 2;
  158.         const int ops_per_thread = 3;
  159.  
  160.         std::vector<std::thread> threads;
  161.  
  162.         // Создаем читателей
  163.         for (int i = 0; i < num_readers; ++i) {
  164.             threads.emplace_back([this, i, ops_per_thread]() {
  165.                 for (int j = 0; j < ops_per_thread; ++j) {
  166.                     rw_lock_.read_lock();
  167.  
  168.                     int data = shared_data_.load();
  169.                     std::this_thread::sleep_for(std::chrono::milliseconds(50));
  170.  
  171.                     std::cout << "Reader " << i+1 << " read: " << data << std::endl;
  172.                     reader_ops_++;
  173.  
  174.                     rw_lock_.read_unlock();
  175.                     std::this_thread::sleep_for(std::chrono::milliseconds(30));
  176.                 }
  177.             });
  178.         }
  179.  
  180.         // Создаем писателей
  181.         for (int i = 0; i < num_writers; ++i) {
  182.             threads.emplace_back([this, i, ops_per_thread]() {
  183.                 for (int j = 0; j < ops_per_thread; ++j) {
  184.                     rw_lock_.write_lock();
  185.  
  186.                     int old_val = shared_data_.load();
  187.                     std::this_thread::sleep_for(std::chrono::milliseconds(80));
  188.                     shared_data_.store(old_val + 1);
  189.  
  190.                     std::cout << "Writer " << i+1 << " changes: "
  191.                               << old_val << " -> " << shared_data_.load() << std::endl;
  192.                     writer_ops_++;
  193.  
  194.                     rw_lock_.write_unlock();
  195.                     std::this_thread::sleep_for(std::chrono::milliseconds(40));
  196.                 }
  197.             });
  198.         }
  199.  
  200.         // Ждем завершения
  201.         for (auto& t : threads) {
  202.             t.join();
  203.         }
  204.  
  205.         // Проверяем результаты
  206.         std::cout << "\nResults 1:" << std::endl;
  207.         std::cout << "Final: " << shared_data_.load() << std::endl;
  208.         std::cout << "Readings: " << reader_ops_.load() << std::endl;
  209.         std::cout << "Changes: " << writer_ops_.load() << std::endl;
  210.         std::cout << "Waitings: " << num_writers * ops_per_thread << std::endl;
  211.  
  212.         bool passed = (writer_ops_.load() == num_writers * ops_per_thread) &&
  213.                      (shared_data_.load() == writer_ops_.load());
  214.         std::cout << "Тест 1: " << (passed ? "OK" : "BAD") << std::endl;
  215.     }
  216.  
  217.     // Тест 2: Предотвращение голодания
  218.     void test_starvation_prevention() {
  219.         std::cout << "\n=== TEST 2 ===" << std::endl;
  220.  
  221.         shared_data_ = 0;
  222.         reader_ops_ = 0;
  223.         writer_ops_ = 0;
  224.         test_running_ = true;
  225.  
  226.         std::vector<std::chrono::steady_clock::time_point> writer_start_times(2);
  227.         std::vector<std::chrono::steady_clock::time_point> writer_end_times(2);
  228.  
  229.         // Создаем много непрерывных читателей
  230.         std::vector<std::thread> reader_threads;
  231.         for (int i = 0; i < 5; ++i) {
  232.             reader_threads.emplace_back([this, i]() {
  233.                 int local_ops = 0;
  234.                 while (test_running_.load() && local_ops < 20) {
  235.                     rw_lock_.read_lock();
  236.  
  237.                     int data = shared_data_.load();
  238.                     std::this_thread::sleep_for(std::chrono::milliseconds(20));
  239.                     reader_ops_++;
  240.                     local_ops++;
  241.  
  242.                     rw_lock_.read_unlock();
  243.                     std::this_thread::sleep_for(std::chrono::milliseconds(10));
  244.                 }
  245.             });
  246.         }
  247.  
  248.         // Создаем писателей с измерением времени ожидания
  249.         std::vector<std::thread> writer_threads;
  250.         for (int i = 0; i < 2; ++i) {
  251.             writer_threads.emplace_back([this, i, &writer_start_times, &writer_end_times]() {
  252.                 std::this_thread::sleep_for(std::chrono::milliseconds(100 + i * 300));
  253.  
  254.                 writer_start_times[i] = std::chrono::steady_clock::now();
  255.                 std::cout << "Writer " << i+1 << " requests..." << std::endl;
  256.  
  257.                 rw_lock_.write_lock();
  258.                 writer_end_times[i] = std::chrono::steady_clock::now();
  259.  
  260.                 int old_val = shared_data_.load();
  261.                 shared_data_.store(old_val + 10);
  262.                 writer_ops_++;
  263.  
  264.                 auto wait_ms = std::chrono::duration_cast<std::chrono::milliseconds>
  265.                               (writer_end_times[i] - writer_start_times[i]).count();
  266.  
  267.                 std::cout << "Writer " << i+1 << " got " << wait_ms
  268.                           << "changes: " << old_val << " -> " << shared_data_.load() << std::endl;
  269.  
  270.                 std::this_thread::sleep_for(std::chrono::milliseconds(50));
  271.                 rw_lock_.write_unlock();
  272.             });
  273.         }
  274.  
  275.         // Запускаем тест на 1.5 секунды
  276.         std::this_thread::sleep_for(std::chrono::milliseconds(1500));
  277.         test_running_ = false;
  278.  
  279.         // Ждем завершения
  280.         for (auto& t : reader_threads) {
  281.             t.join();
  282.         }
  283.         for (auto& t : writer_threads) {
  284.             t.join();
  285.         }
  286.  
  287.         // Анализируем результаты
  288.         std::cout << "\Results 2:" << std::endl;
  289.         std::cout << "Readings: " << reader_ops_.load() << std::endl;
  290.         std::cout << "Changes: " << writer_ops_.load() << std::endl;
  291.         std::cout << "Final: " << shared_data_.load() << std::endl;
  292.  
  293.         std::cout << "Waiting time:" << std::endl;
  294.         for (int i = 0; i < 2; ++i) {
  295.             if (writer_end_times[i] > writer_start_times[i]) {
  296.                 auto wait_ms = std::chrono::duration_cast<std::chrono::milliseconds>
  297.                               (writer_end_times[i] - writer_start_times[i]).count();
  298.                 std::cout << "  Writer " << i+1 << ": " << wait_ms << "мс" << std::endl;
  299.             }
  300.         }
  301.  
  302.         bool no_starvation = (writer_ops_.load() == 2) && (shared_data_.load() == 20);
  303.         std::cout << "Starving: " << (no_starvation ? "OK" : "BAD") << std::endl;
  304.     }
  305.  
  306.     // Тест 3: Параллельные читатели
  307.     void test_concurrent_readers() {
  308.         std::cout << "\n=== TEST 3 ===" << std::endl;
  309.  
  310.         shared_data_ = 42;
  311.         reader_ops_ = 0;
  312.  
  313.         const int num_readers = 6;
  314.         std::vector<std::thread> threads;
  315.  
  316.         auto start_time = std::chrono::steady_clock::now();
  317.  
  318.         // Создаем читателей которые должны работать параллельно
  319.         for (int i = 0; i < num_readers; ++i) {
  320.             threads.emplace_back([this, i]() {
  321.                 rw_lock_.read_lock();
  322.  
  323.                 auto thread_start = std::chrono::steady_clock::now();
  324.                 std::cout << "Reader " << i+1 << " starts" << std::endl;
  325.  
  326.                 // Имитируем долгое чтение
  327.                 std::this_thread::sleep_for(std::chrono::milliseconds(200));
  328.                 int data = shared_data_.load();
  329.  
  330.                 auto thread_end = std::chrono::steady_clock::now();
  331.                 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>
  332.                                (thread_end - thread_start).count();
  333.  
  334.                 std::cout << "Reader " << i+1 << " ends "
  335.                           << duration << "data: " << data << std::endl;
  336.                 reader_ops_++;
  337.  
  338.                 rw_lock_.read_unlock();
  339.             });
  340.         }
  341.  
  342.         for (auto& t : threads) {
  343.             t.join();
  344.         }
  345.  
  346.         auto end_time = std::chrono::steady_clock::now();
  347.         auto total_time = std::chrono::duration_cast<std::chrono::milliseconds>
  348.                          (end_time - start_time).count();
  349.  
  350.         std::cout << "\nResults 3:" << std::endl;
  351.         std::cout << "Time: " << total_time << "мс" << std::endl;
  352.         std::cout << "Readings: " << reader_ops_.load() << std::endl;
  353.  
  354.         bool concurrent = total_time < 400; // Даем запас
  355.         std::cout << "Reading: " << (concurrent ? "OK" : "BAD") << std::endl;
  356.     }
  357.  
  358.     // Запуск всех тестов
  359.     void run_all_tests() {
  360.         std::cout << "=== READER-WRITER LOCK ===" << std::endl;
  361.         std::cout << "Ticket System + FIFO Queue" << std::endl;
  362.  
  363.         test_basic_functionality();
  364.  
  365.         std::this_thread::sleep_for(std::chrono::milliseconds(500));
  366.         test_concurrent_readers();
  367.  
  368.         std::this_thread::sleep_for(std::chrono::milliseconds(500));  
  369.         test_starvation_prevention();
  370.     }
  371. };
  372.  
  373. // Основное тело
  374. int main() {
  375.     try {
  376.         RWLockTester tester;
  377.         tester.run_all_tests();
  378.  
  379.         std::cout << "✓ Ticket System" << std::endl;
  380.         std::cout << "✓ FIFO Queue" << std::endl;
  381.         std::cout << "✓ Starving System" << std::endl;
  382.         std::cout << "✓ std::condition_variable without busy waiting" << std::endl;
  383.         std::cout << "✓ RW_Lock Class" << std::endl;
  384.         std::cout << "✓ Thread-safe" << std::endl;
  385.  
  386.     } catch (const std::exception& e) {
  387.         std::cerr << "Error: " << e.what() << std::endl;
  388.         return 1;
  389.     }
  390.  
  391.     return 0;
  392. }
Advertisement
Add Comment
Please, Sign In to add comment