Advertisement
artmexbet

fifth_task

May 11th, 2024
537
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.03 KB | None | 0 0
  1. #include <iostream>
  2. #include <vector>
  3. #include <semaphore.h>
  4. #include <atomic>
  5. #include <thread>
  6.  
  7. class TableToClean {
  8. public:
  9.     int tableNumber;
  10.     int guestNumber;
  11.  
  12.     TableToClean(int tableNumber, int guestNumber) {
  13.         this->tableNumber = tableNumber;
  14.         this->guestNumber = guestNumber;
  15.     }
  16. };
  17.  
  18. std::vector<std::vector<bool>> matrix;
  19.  
  20. std::vector<TableToClean> tablesToClean;
  21.  
  22. sem_t queueSem;  // Семафор для количества людей в очереди (по нему продавцы ориентируются на наличие необработанных покупателей)
  23. pthread_mutex_t queueMutex;  // Мьютекс для чтения и записи в очередь
  24. pthread_mutex_t tableMutex;  // Мьютекс для работы с таблицами
  25. sem_t freeTablesSem;  // Семафор для количества свободных столов
  26. sem_t cleanTablesSem;  // Семафор для количества столов, которые нужно убрать
  27. pthread_mutex_t cleanTablesMutex;  // Мьютекс для работы со столами, которые нужно убрать
  28.  
  29. pthread_rwlock_t rwlock;  // Рид-райт лок для чтения и записи в консоль
  30.  
  31. std::atomic<bool> stop_thread(false);  // Создаём "атомарную" переменную. Это чуть-чуть упрощает работу с потоками.
  32. // Если эта переменная - true, все потоки умирают.
  33.  
  34. class GuestInfo {
  35. public:
  36.     int tableNumber;
  37.     int guestNumber;
  38.     int eatTime;
  39.  
  40.     GuestInfo(int tableNumber, int guestNumber, int eatTime) {
  41.         this->tableNumber = tableNumber;
  42.         this->guestNumber = guestNumber;
  43.         this->eatTime = eatTime;
  44.     }
  45. };
  46.  
  47. void *GuestActor(void *arg) {
  48.     auto *info = (GuestInfo *) arg;
  49.  
  50.     pthread_rwlock_wrlock(&rwlock);
  51.     std::cout << "Guest " << info->guestNumber << " started eating at table " << info->tableNumber << std::endl;
  52.     pthread_rwlock_unlock(&rwlock);
  53.  
  54.     std::this_thread::sleep_for(std::chrono::seconds(info->eatTime));  // Гость ест
  55.  
  56.     pthread_mutex_lock(&cleanTablesMutex);  // Лочим мьютекс
  57.     tablesToClean.emplace_back(info->tableNumber, info->guestNumber);  // Добавляем стол в список для уборки
  58.     pthread_mutex_unlock(&cleanTablesMutex);  // Разлочим мьютекс
  59.     sem_post(&cleanTablesSem);  // Освобождаем семафор для уборки
  60.  
  61.     pthread_rwlock_wrlock(&rwlock);  // Лочим рид-райт лок для записи в консоль
  62.     std::cout << "Guest " << info->guestNumber << " finished eating at table " << info->tableNumber << std::endl;
  63.     pthread_rwlock_unlock(&rwlock);  // Разлочим рид-райт лок для записи в консоль
  64.     delete info;
  65.     return nullptr;
  66. }
  67.  
  68. class Guest {
  69. public:
  70.     int checkoutTime;
  71.     int eatTime;
  72.  
  73.     Guest(int checkoutTime, int eatTime) {
  74.         this->checkoutTime = checkoutTime;
  75.         this->eatTime = eatTime;
  76.     }
  77.  
  78.     void start(int tableNumber, int guestNumber) {
  79.         pthread_t thread;
  80.         auto *info = new GuestInfo(tableNumber, guestNumber, this->eatTime);
  81.         pthread_create(&thread, nullptr, &GuestActor, info);
  82.     }
  83. };
  84.  
  85. std::vector<Guest> queue;
  86.  
  87. void *SellerActor(void *arg) {
  88.     int *sellerId = (int *) arg;
  89.     while (!stop_thread) {
  90.         sem_wait(&queueSem);  // Ждем, пока в очереди не появится хоть один покупатель
  91.         pthread_mutex_lock(&queueMutex);  // Лочим мьютекс
  92.         if (queue.empty()) {
  93.             pthread_mutex_unlock(&queueMutex);  // Разлочим мьютекс
  94.             continue;
  95.         }
  96.         auto guest = queue[0];
  97.         queue.erase(queue.begin());
  98.         pthread_mutex_unlock(&queueMutex);  // Разлочим мьютекс
  99.  
  100.         pthread_rwlock_wrlock(&rwlock);  // Лочим рид-райт лок для записи в консоль
  101.         std::cout << "Seller " << sellerId << " started serving guest" << std::endl;
  102.         pthread_rwlock_unlock(&rwlock);  // Разлочим рид-райт лок для записи в консоль
  103.  
  104.         std::this_thread::sleep_for(
  105.                 std::chrono::seconds(guest.checkoutTime));  // Продавец ждет, пока покупатель сделает заказ
  106.  
  107.         pthread_mutex_lock(&tableMutex);  // Лочим мьютекс
  108.  
  109.         sem_wait(&freeTablesSem);  // Ждём, пока не освободится стол
  110.  
  111.         pthread_rwlock_wrlock(&rwlock);  // Лочим рид-райт лок для записи в консоль
  112.         std::cout << "Seller " << sellerId << " finished serving guest" << std::endl;
  113.         pthread_rwlock_unlock(&rwlock);  // Разлочим рид-райт лок для записи в консоль
  114.  
  115.         // Ищем свободное место
  116.         bool found = false;
  117.         for (int i = 0; i < matrix.size(); i++) {
  118.             for (int j = 0; j < matrix[i].size(); j++) {
  119.                 if (!matrix[i][j]) {
  120.                     pthread_rwlock_wrlock(&rwlock);  // Лочим рид-райт лок для записи в консоль
  121.                     std::cout << "Guest " << j << " sits at table " << i << std::endl;
  122.                     pthread_rwlock_unlock(&rwlock);  // Разлочим рид-райт лок для записи в консоль
  123.                     matrix[i][j] = true;
  124.                     pthread_mutex_unlock(&tableMutex);  // Разлочим мьютекс
  125.                     guest.start(i, j);
  126.                     found = true;
  127.                     break;
  128.                 }
  129.             }
  130.             if (found) {
  131.                 break;
  132.             }
  133.         }
  134.     }
  135.     return nullptr;
  136. }
  137.  
  138. void *CleanerActor(void *arg) {
  139.     int *cleanTime = (int *) arg;
  140.     while (!stop_thread) {
  141.         sem_wait(&cleanTablesSem);  // Ждем, пока появится стол, который нужно убрать
  142.         pthread_mutex_lock(&cleanTablesMutex);  // Лочим мьютекс
  143.         if (tablesToClean.empty()) {
  144.             pthread_mutex_unlock(&cleanTablesMutex);  // Разлочим мьютекс
  145.             continue;
  146.         }
  147.         auto table = tablesToClean[0];
  148.         tablesToClean.erase(tablesToClean.begin());
  149.         pthread_mutex_unlock(&cleanTablesMutex);  // Разлочим мьютекс
  150.  
  151.         pthread_rwlock_wrlock(&rwlock);  // Лочим рид-райт лок для записи в консоль
  152.         std::cout << "Cleaner started cleaning table " << table.tableNumber << " from guest " << table.guestNumber
  153.                   << std::endl;
  154.         pthread_rwlock_unlock(&rwlock);  // Разлочим рид-райт лок для записи в консоль
  155.  
  156.         std::this_thread::sleep_for(std::chrono::seconds(*cleanTime));  // Убираем стол
  157.  
  158.         pthread_mutex_lock(&tableMutex);  // Лочим мьютекс
  159.         matrix[table.tableNumber][table.guestNumber] = false;  // Освобождаем стол
  160.         sem_post(&freeTablesSem);  // Освобождаем семафор для свободных столов
  161.         pthread_mutex_unlock(&tableMutex);  // Разлочим мьютекс
  162.  
  163.         pthread_rwlock_wrlock(&rwlock);  // Лочим рид-райт лок для записи в консоль
  164.         std::cout << "Cleaner finished cleaning table " << table.tableNumber << " from guest " << table.guestNumber
  165.                   << std::endl;
  166.         pthread_rwlock_unlock(&rwlock);  // Разлочим рид-райт лок для записи в консоль
  167.     }
  168.     return nullptr;
  169. }
  170.  
  171. void fillMatrix(int tableCount, int tableSize) {
  172.     matrix.resize(tableCount);
  173.     for (int i = 0; i < tableCount; i++) {
  174.         matrix[i].resize(tableSize);
  175.         for (int j = 0; j < tableSize; j++) {
  176.             matrix[i][j] = false;
  177.         }
  178.     }
  179. }
  180.  
  181. void printCommands() {
  182.     std::cout << "----------------------------------" << std::endl;
  183.     std::cout << "Commands:" << std::endl;
  184.     std::cout << "1. Add guest" << std::endl;
  185.     std::cout << "2. Print tables" << std::endl;
  186.     std::cout << "3. Print queue" << std::endl;
  187.     std::cout << "4. Exit" << std::endl;
  188.     std::cout << "5. Print clean queue" << std::endl;
  189.     std::cout << "Enter number of commands:" << std::endl;
  190.     std::cout << "----------------------------------" << std::endl;
  191. }
  192.  
  193. void printTables() {
  194.     for (int i = 0; i < matrix.size(); i++) {
  195.         std::cout << "Table " << i << ": ";
  196.         for (auto &&j: matrix[i]) {
  197.             if (j) {
  198.                 std::cout << "X";  // Принтуем столы, которые заняты
  199.             } else {
  200.                 std::cout << "O";  // Принтуем свободные столы
  201.             }
  202.         }
  203.         std::cout << std::endl;
  204.     }
  205. }
  206.  
  207. void printQueue() {
  208.     if (queue.empty()){
  209.         std::cout << "Queue is empty" << std::endl;
  210.         return;
  211.     }
  212.     for (int i = 0; i < queue.size(); i++) {
  213.         std::cout << "Guest " << i << ": ";
  214.         std::cout << "Checkout time: " << queue[i].checkoutTime << " Eat time: " << queue[i].eatTime << std::endl;
  215.     }
  216. }
  217.  
  218. void printCleanQueue() {
  219.     if (tablesToClean.empty()){
  220.         std::cout << "Clean queue is empty" << std::endl;
  221.         return;
  222.     }
  223.     for (auto & i : tablesToClean) {
  224.         std::cout << "Table " << i.tableNumber << " from guest " << i.guestNumber << std::endl;
  225.     }
  226. }
  227.  
  228. int main() {
  229.     sem_init(&queueSem, 0, 0);  // Инициализируем семафор для количества людей в очереди
  230.     // К нему подключаются продавцы, ожидающие покупателей
  231.     pthread_mutex_init(&queueMutex, nullptr);
  232.     // Инициализируем мьютекс для работы с очередью (добавление и удаление элементов)
  233.     pthread_mutex_init(&tableMutex, nullptr);
  234.  
  235.     pthread_mutex_init(&cleanTablesMutex,
  236.                        nullptr);  // Инициализируем мьютекс для работы со столами, которые нужно убрать
  237.     sem_init(&cleanTablesSem, 0, 0);  // Инициализируем семафор для количества столов, которые нужно убрать
  238.  
  239.     pthread_rwlock_init(&rwlock, nullptr);  // Инициализируем рид-райт лок для чтения и записи в консоль
  240.  
  241.     // Создаём потоки продавцов
  242.  
  243.     pthread_t firstSellerThread, secondSellerThread;
  244.     pthread_create(&firstSellerThread, nullptr, &SellerActor, (void *) 1);
  245.     pthread_create(&secondSellerThread, nullptr, &SellerActor, (void *) 2);
  246.  
  247.  
  248.     std::cout << "Enter table count" << std::endl;
  249.     int tableCount;
  250.     std::cin >> tableCount;
  251.     std::cout << "Enter table size" << std::endl;
  252.     int tableSize;
  253.     std::cin >> tableSize;
  254.     fillMatrix(tableCount, tableSize);
  255.  
  256.     sem_init(&freeTablesSem, 1, tableCount * tableSize);  // Инициализируем семафор для количества свободных столов
  257.  
  258.     std::cout << "Enter clean time in seconds" << std::endl;
  259.     int cleanTime;
  260.     std::cin >> cleanTime; // Вводим время, которое потребуется для мытья одной тарелки (считаем для упрощения,
  261.     // что каждый гость делает одинаковое количество грязной посуды)
  262.  
  263.     // Создаём потоки уборщиков
  264.     pthread_t firstCleanerThread;
  265.     pthread_create(&firstCleanerThread, nullptr, &CleanerActor, &cleanTime);
  266.  
  267.     while (!stop_thread) {
  268.         pthread_rwlock_wrlock(&rwlock);  // Лочим рид-райт лок для записи в консоль
  269.         printCommands();
  270.         pthread_rwlock_unlock(&rwlock);  // Разлочим рид-райт лок для записи в консоль
  271.         int command;
  272.         std::cin >> command;
  273.         pthread_rwlock_wrlock(&rwlock);  // Лочим рид-райт лок для записи в консоль
  274.         if (command == 1) {
  275.             // Добавляем нового покупателя
  276.             std::cout << "Enter checkout time" << std::endl;
  277.             int checkoutTime;
  278.             std::cin >> checkoutTime; // Вводим время заказа покупателя (время, которое он потратит на создание заказа)
  279.  
  280.             std::cout << "Enter eat time" << std::endl;
  281.             int eatTime;
  282.             std::cin >> eatTime;// Вводим время еды покупателя (время, которое он потратит на поедание заказа)
  283.  
  284.             Guest guest(checkoutTime, eatTime);
  285.             pthread_mutex_lock(&queueMutex); // Лочим мьютекс
  286.             queue.push_back(guest);
  287.             printQueue();
  288.             pthread_mutex_unlock(&queueMutex); // Разлочим мьютекс
  289.             sem_post(&queueSem);
  290.         } else if (command == 2) {
  291.             pthread_mutex_lock(&tableMutex);
  292.             printTables();
  293.             pthread_mutex_unlock(&tableMutex);
  294.         } else if (command == 3) {
  295.             pthread_mutex_lock(&queueMutex);
  296.             printQueue();
  297.             pthread_mutex_unlock(&queueMutex);
  298.         } else if (command == 4) {
  299.             stop_thread = true;
  300.         } else if(command == 5) {
  301.             pthread_mutex_lock(&cleanTablesMutex);
  302.             printCleanQueue();
  303.             pthread_mutex_unlock(&cleanTablesMutex);
  304.         } else {
  305.             std::cout << "Invalid command" << std::endl;
  306.         }
  307.         pthread_rwlock_unlock(&rwlock);  // Разлочим рид-райт лок для записи в консоль
  308.     }
  309.     // Тут можно заджоинить потоки между собой
  310.     return 0;
  311. }
  312.  
Advertisement
Comments
  • artmexbet
    11 days
    # text 5.61 KB | 0 0
    1. Уфф, ну поехали!
    2.  
    3. Общее описание работы:
    4. На старте вводятся количество столов и мест за столом (N и M)
    5. Затем время уборки за каждым человеком
    6. Далее создаются 2 потока для продавцов и один для уборщика (их описание будет позже)
    7. Теперь пользователю доступна менюшка для управления всем этим добром.
    8. Можно добавить гостя, после ввода его параметров он добавится в очередь
    9. и будет обработан первым освободившимся продавцом.
    10. Затем гость выбирает еду на протяжении checkoutTime секунд (в это время продавец ждёт, пока гость не выберет).
    11. После того как гость выбрал, он садится за первый свободный стол, продавец освобождается и может принимать следующего.
    12. Когда гость доедает, его стол добавляется в очередь чистки, и уборщик, если он свободен, начинает его убирать
    13. (уборщик закончит уборку через cleanTime после начала). После уборки стол освобождается и начинает принимать новых гостей.
    14.  
    15. Описание работы продавца:
    16. Поток останавливается, пока количество людей в очереди (значение семафора queueSem) не станет >0.
    17. Как только это происходит, продавец лочит очередь для безопасного чтения и пытается достать оттуда гостя.
    18. Затем продавец открывает очередь и ждёт checkoutTime секунд, пока гость не выберет заказ.
    19. После этого садит его за ближайший свободный стол, предварительно заблокировав tableMutex для безопасной записи.
    20. Далее логика поедания переходит пользователю.
    21.  
    22. Описание работы гостя:
    23. Гость ест на протяжении eatTime секунд, затем лочит cleanTablesMutex для добавления стола в очередь для уборки.
    24. Затем увеличивает cleanTablesSem, чтобы освободить работу потока уборщика.
    25.  
    26. Описание работы уборщика:
    27. Уборщик ждёт, пока количество столов для уборки (значение семафора cleanTablesSem) не станет >0.
    28. Затем получает первый в очереди грязный стол, предварительно залочив мьютекс работы с грязными столами,
    29. и начинает его убирать на протяжении cleanTime секунд.
    30. После этого отмечает стол как свободный и ждёт следующего стола для уборки.
    31.  
    32. Общие приколы:
    33. Зачем используются мьютексы в связке с семафорами у Уборщика и Продавцов? Эту же работу можно было выполнить,
    34. используя только мьютексы, проверяя, например, каждую секунду, есть ли в очереди (людей или столов для уборки)
    35. элементы, однако это тормозило бы работу, так как приходилось бы выделять время на работу потоков продавца и уборщика впустую.
    36. В то же время при использовании семафоров совместно с мьютексами появляется возможность "усыпить" потоки до того момента,
    37. как в очереди появится хотя бы один элемент, не проверяя каждые n секунд, есть ли свободный элемент.
    38. Использовать только семафоры так же не представлялось возможным, т.к. при таком варианте появляется возможность того,
    39. что 2 потока возьмут в работу одного и того же пользователя.
    40.  
    41. Ещё используется блокировка чтения/записи при записи текста в консоль. Это необходимо, поскольку все потоки могут туда писать,
    42. и запись может перемешиваться.
    43.  
    44. В основном потоке, а также потоках продавцов и уборщика используется атомарная переменная stop_thread.
    45. Для справки: атомарная переменная обеспечивает безопасные чтение и запись в неё без использования мьютексов и прочего.
    46. Она здесь для того, чтобы по завершению потока main завершались и дочерние потоки, закончив все свои дела.
    47.  
Add Comment
Please, Sign In to add comment
Advertisement