Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <iostream>
- #include <vector>
- #include <semaphore.h>
- #include <atomic>
- #include <thread>
- class TableToClean {
- public:
- int tableNumber;
- int guestNumber;
- TableToClean(int tableNumber, int guestNumber) {
- this->tableNumber = tableNumber;
- this->guestNumber = guestNumber;
- }
- };
- std::vector<std::vector<bool>> matrix;
- std::vector<TableToClean> tablesToClean;
- sem_t queueSem; // Семафор для количества людей в очереди (по нему продавцы ориентируются на наличие необработанных покупателей)
- pthread_mutex_t queueMutex; // Мьютекс для чтения и записи в очередь
- pthread_mutex_t tableMutex; // Мьютекс для работы с таблицами
- sem_t freeTablesSem; // Семафор для количества свободных столов
- sem_t cleanTablesSem; // Семафор для количества столов, которые нужно убрать
- pthread_mutex_t cleanTablesMutex; // Мьютекс для работы со столами, которые нужно убрать
- pthread_rwlock_t rwlock; // Рид-райт лок для чтения и записи в консоль
- std::atomic<bool> stop_thread(false); // Создаём "атомарную" переменную. Это чуть-чуть упрощает работу с потоками.
- // Если эта переменная - true, все потоки умирают.
- class GuestInfo {
- public:
- int tableNumber;
- int guestNumber;
- int eatTime;
- GuestInfo(int tableNumber, int guestNumber, int eatTime) {
- this->tableNumber = tableNumber;
- this->guestNumber = guestNumber;
- this->eatTime = eatTime;
- }
- };
- void *GuestActor(void *arg) {
- auto *info = (GuestInfo *) arg;
- pthread_rwlock_wrlock(&rwlock);
- std::cout << "Guest " << info->guestNumber << " started eating at table " << info->tableNumber << std::endl;
- pthread_rwlock_unlock(&rwlock);
- std::this_thread::sleep_for(std::chrono::seconds(info->eatTime)); // Гость ест
- pthread_mutex_lock(&cleanTablesMutex); // Лочим мьютекс
- tablesToClean.emplace_back(info->tableNumber, info->guestNumber); // Добавляем стол в список для уборки
- pthread_mutex_unlock(&cleanTablesMutex); // Разлочим мьютекс
- sem_post(&cleanTablesSem); // Освобождаем семафор для уборки
- pthread_rwlock_wrlock(&rwlock); // Лочим рид-райт лок для записи в консоль
- std::cout << "Guest " << info->guestNumber << " finished eating at table " << info->tableNumber << std::endl;
- pthread_rwlock_unlock(&rwlock); // Разлочим рид-райт лок для записи в консоль
- delete info;
- return nullptr;
- }
- class Guest {
- public:
- int checkoutTime;
- int eatTime;
- Guest(int checkoutTime, int eatTime) {
- this->checkoutTime = checkoutTime;
- this->eatTime = eatTime;
- }
- void start(int tableNumber, int guestNumber) {
- pthread_t thread;
- auto *info = new GuestInfo(tableNumber, guestNumber, this->eatTime);
- pthread_create(&thread, nullptr, &GuestActor, info);
- }
- };
- std::vector<Guest> queue;
- void *SellerActor(void *arg) {
- int *sellerId = (int *) arg;
- while (!stop_thread) {
- sem_wait(&queueSem); // Ждем, пока в очереди не появится хоть один покупатель
- pthread_mutex_lock(&queueMutex); // Лочим мьютекс
- if (queue.empty()) {
- pthread_mutex_unlock(&queueMutex); // Разлочим мьютекс
- continue;
- }
- auto guest = queue[0];
- queue.erase(queue.begin());
- pthread_mutex_unlock(&queueMutex); // Разлочим мьютекс
- pthread_rwlock_wrlock(&rwlock); // Лочим рид-райт лок для записи в консоль
- std::cout << "Seller " << sellerId << " started serving guest" << std::endl;
- pthread_rwlock_unlock(&rwlock); // Разлочим рид-райт лок для записи в консоль
- std::this_thread::sleep_for(
- std::chrono::seconds(guest.checkoutTime)); // Продавец ждет, пока покупатель сделает заказ
- pthread_mutex_lock(&tableMutex); // Лочим мьютекс
- sem_wait(&freeTablesSem); // Ждём, пока не освободится стол
- pthread_rwlock_wrlock(&rwlock); // Лочим рид-райт лок для записи в консоль
- std::cout << "Seller " << sellerId << " finished serving guest" << std::endl;
- pthread_rwlock_unlock(&rwlock); // Разлочим рид-райт лок для записи в консоль
- // Ищем свободное место
- bool found = false;
- for (int i = 0; i < matrix.size(); i++) {
- for (int j = 0; j < matrix[i].size(); j++) {
- if (!matrix[i][j]) {
- pthread_rwlock_wrlock(&rwlock); // Лочим рид-райт лок для записи в консоль
- std::cout << "Guest " << j << " sits at table " << i << std::endl;
- pthread_rwlock_unlock(&rwlock); // Разлочим рид-райт лок для записи в консоль
- matrix[i][j] = true;
- pthread_mutex_unlock(&tableMutex); // Разлочим мьютекс
- guest.start(i, j);
- found = true;
- break;
- }
- }
- if (found) {
- break;
- }
- }
- }
- return nullptr;
- }
- void *CleanerActor(void *arg) {
- int *cleanTime = (int *) arg;
- while (!stop_thread) {
- sem_wait(&cleanTablesSem); // Ждем, пока появится стол, который нужно убрать
- pthread_mutex_lock(&cleanTablesMutex); // Лочим мьютекс
- if (tablesToClean.empty()) {
- pthread_mutex_unlock(&cleanTablesMutex); // Разлочим мьютекс
- continue;
- }
- auto table = tablesToClean[0];
- tablesToClean.erase(tablesToClean.begin());
- pthread_mutex_unlock(&cleanTablesMutex); // Разлочим мьютекс
- pthread_rwlock_wrlock(&rwlock); // Лочим рид-райт лок для записи в консоль
- std::cout << "Cleaner started cleaning table " << table.tableNumber << " from guest " << table.guestNumber
- << std::endl;
- pthread_rwlock_unlock(&rwlock); // Разлочим рид-райт лок для записи в консоль
- std::this_thread::sleep_for(std::chrono::seconds(*cleanTime)); // Убираем стол
- pthread_mutex_lock(&tableMutex); // Лочим мьютекс
- matrix[table.tableNumber][table.guestNumber] = false; // Освобождаем стол
- sem_post(&freeTablesSem); // Освобождаем семафор для свободных столов
- pthread_mutex_unlock(&tableMutex); // Разлочим мьютекс
- pthread_rwlock_wrlock(&rwlock); // Лочим рид-райт лок для записи в консоль
- std::cout << "Cleaner finished cleaning table " << table.tableNumber << " from guest " << table.guestNumber
- << std::endl;
- pthread_rwlock_unlock(&rwlock); // Разлочим рид-райт лок для записи в консоль
- }
- return nullptr;
- }
- void fillMatrix(int tableCount, int tableSize) {
- matrix.resize(tableCount);
- for (int i = 0; i < tableCount; i++) {
- matrix[i].resize(tableSize);
- for (int j = 0; j < tableSize; j++) {
- matrix[i][j] = false;
- }
- }
- }
- void printCommands() {
- std::cout << "----------------------------------" << std::endl;
- std::cout << "Commands:" << std::endl;
- std::cout << "1. Add guest" << std::endl;
- std::cout << "2. Print tables" << std::endl;
- std::cout << "3. Print queue" << std::endl;
- std::cout << "4. Exit" << std::endl;
- std::cout << "5. Print clean queue" << std::endl;
- std::cout << "Enter number of commands:" << std::endl;
- std::cout << "----------------------------------" << std::endl;
- }
- void printTables() {
- for (int i = 0; i < matrix.size(); i++) {
- std::cout << "Table " << i << ": ";
- for (auto &&j: matrix[i]) {
- if (j) {
- std::cout << "X"; // Принтуем столы, которые заняты
- } else {
- std::cout << "O"; // Принтуем свободные столы
- }
- }
- std::cout << std::endl;
- }
- }
- void printQueue() {
- if (queue.empty()){
- std::cout << "Queue is empty" << std::endl;
- return;
- }
- for (int i = 0; i < queue.size(); i++) {
- std::cout << "Guest " << i << ": ";
- std::cout << "Checkout time: " << queue[i].checkoutTime << " Eat time: " << queue[i].eatTime << std::endl;
- }
- }
- void printCleanQueue() {
- if (tablesToClean.empty()){
- std::cout << "Clean queue is empty" << std::endl;
- return;
- }
- for (auto & i : tablesToClean) {
- std::cout << "Table " << i.tableNumber << " from guest " << i.guestNumber << std::endl;
- }
- }
- int main() {
- sem_init(&queueSem, 0, 0); // Инициализируем семафор для количества людей в очереди
- // К нему подключаются продавцы, ожидающие покупателей
- pthread_mutex_init(&queueMutex, nullptr);
- // Инициализируем мьютекс для работы с очередью (добавление и удаление элементов)
- pthread_mutex_init(&tableMutex, nullptr);
- pthread_mutex_init(&cleanTablesMutex,
- nullptr); // Инициализируем мьютекс для работы со столами, которые нужно убрать
- sem_init(&cleanTablesSem, 0, 0); // Инициализируем семафор для количества столов, которые нужно убрать
- pthread_rwlock_init(&rwlock, nullptr); // Инициализируем рид-райт лок для чтения и записи в консоль
- // Создаём потоки продавцов
- pthread_t firstSellerThread, secondSellerThread;
- pthread_create(&firstSellerThread, nullptr, &SellerActor, (void *) 1);
- pthread_create(&secondSellerThread, nullptr, &SellerActor, (void *) 2);
- std::cout << "Enter table count" << std::endl;
- int tableCount;
- std::cin >> tableCount;
- std::cout << "Enter table size" << std::endl;
- int tableSize;
- std::cin >> tableSize;
- fillMatrix(tableCount, tableSize);
- sem_init(&freeTablesSem, 1, tableCount * tableSize); // Инициализируем семафор для количества свободных столов
- std::cout << "Enter clean time in seconds" << std::endl;
- int cleanTime;
- std::cin >> cleanTime; // Вводим время, которое потребуется для мытья одной тарелки (считаем для упрощения,
- // что каждый гость делает одинаковое количество грязной посуды)
- // Создаём потоки уборщиков
- pthread_t firstCleanerThread;
- pthread_create(&firstCleanerThread, nullptr, &CleanerActor, &cleanTime);
- while (!stop_thread) {
- pthread_rwlock_wrlock(&rwlock); // Лочим рид-райт лок для записи в консоль
- printCommands();
- pthread_rwlock_unlock(&rwlock); // Разлочим рид-райт лок для записи в консоль
- int command;
- std::cin >> command;
- pthread_rwlock_wrlock(&rwlock); // Лочим рид-райт лок для записи в консоль
- if (command == 1) {
- // Добавляем нового покупателя
- std::cout << "Enter checkout time" << std::endl;
- int checkoutTime;
- std::cin >> checkoutTime; // Вводим время заказа покупателя (время, которое он потратит на создание заказа)
- std::cout << "Enter eat time" << std::endl;
- int eatTime;
- std::cin >> eatTime;// Вводим время еды покупателя (время, которое он потратит на поедание заказа)
- Guest guest(checkoutTime, eatTime);
- pthread_mutex_lock(&queueMutex); // Лочим мьютекс
- queue.push_back(guest);
- printQueue();
- pthread_mutex_unlock(&queueMutex); // Разлочим мьютекс
- sem_post(&queueSem);
- } else if (command == 2) {
- pthread_mutex_lock(&tableMutex);
- printTables();
- pthread_mutex_unlock(&tableMutex);
- } else if (command == 3) {
- pthread_mutex_lock(&queueMutex);
- printQueue();
- pthread_mutex_unlock(&queueMutex);
- } else if (command == 4) {
- stop_thread = true;
- } else if(command == 5) {
- pthread_mutex_lock(&cleanTablesMutex);
- printCleanQueue();
- pthread_mutex_unlock(&cleanTablesMutex);
- } else {
- std::cout << "Invalid command" << std::endl;
- }
- pthread_rwlock_unlock(&rwlock); // Разлочим рид-райт лок для записи в консоль
- }
- // Тут можно заджоинить потоки между собой
- return 0;
- }
Advertisement
Comments
-
- Уфф, ну поехали!
- Общее описание работы:
- На старте вводятся количество столов и мест за столом (N и M)
- Затем время уборки за каждым человеком
- Далее создаются 2 потока для продавцов и один для уборщика (их описание будет позже)
- Теперь пользователю доступна менюшка для управления всем этим добром.
- Можно добавить гостя, после ввода его параметров он добавится в очередь
- и будет обработан первым освободившимся продавцом.
- Затем гость выбирает еду на протяжении checkoutTime секунд (в это время продавец ждёт, пока гость не выберет).
- После того как гость выбрал, он садится за первый свободный стол, продавец освобождается и может принимать следующего.
- Когда гость доедает, его стол добавляется в очередь чистки, и уборщик, если он свободен, начинает его убирать
- (уборщик закончит уборку через cleanTime после начала). После уборки стол освобождается и начинает принимать новых гостей.
- Описание работы продавца:
- Поток останавливается, пока количество людей в очереди (значение семафора queueSem) не станет >0.
- Как только это происходит, продавец лочит очередь для безопасного чтения и пытается достать оттуда гостя.
- Затем продавец открывает очередь и ждёт checkoutTime секунд, пока гость не выберет заказ.
- После этого садит его за ближайший свободный стол, предварительно заблокировав tableMutex для безопасной записи.
- Далее логика поедания переходит пользователю.
- Описание работы гостя:
- Гость ест на протяжении eatTime секунд, затем лочит cleanTablesMutex для добавления стола в очередь для уборки.
- Затем увеличивает cleanTablesSem, чтобы освободить работу потока уборщика.
- Описание работы уборщика:
- Уборщик ждёт, пока количество столов для уборки (значение семафора cleanTablesSem) не станет >0.
- Затем получает первый в очереди грязный стол, предварительно залочив мьютекс работы с грязными столами,
- и начинает его убирать на протяжении cleanTime секунд.
- После этого отмечает стол как свободный и ждёт следующего стола для уборки.
- Общие приколы:
- Зачем используются мьютексы в связке с семафорами у Уборщика и Продавцов? Эту же работу можно было выполнить,
- используя только мьютексы, проверяя, например, каждую секунду, есть ли в очереди (людей или столов для уборки)
- элементы, однако это тормозило бы работу, так как приходилось бы выделять время на работу потоков продавца и уборщика впустую.
- В то же время при использовании семафоров совместно с мьютексами появляется возможность "усыпить" потоки до того момента,
- как в очереди появится хотя бы один элемент, не проверяя каждые n секунд, есть ли свободный элемент.
- Использовать только семафоры так же не представлялось возможным, т.к. при таком варианте появляется возможность того,
- что 2 потока возьмут в работу одного и того же пользователя.
- Ещё используется блокировка чтения/записи при записи текста в консоль. Это необходимо, поскольку все потоки могут туда писать,
- и запись может перемешиваться.
- В основном потоке, а также потоках продавцов и уборщика используется атомарная переменная stop_thread.
- Для справки: атомарная переменная обеспечивает безопасные чтение и запись в неё без использования мьютексов и прочего.
- Она здесь для того, чтобы по завершению потока main завершались и дочерние потоки, закончив все свои дела.
Add Comment
Please, Sign In to add comment
Advertisement