Advertisement
Tark_Wight

Untitled

Apr 15th, 2023
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.88 KB | None | 0 0
  1. #include <iostream>
  2. #include <thread>
  3. #include <Windows.h>
  4. #include <mutex>
  5. #include <string>
  6. #include <vector>
  7. #include <ctime>
  8. #include <chrono>
  9. #include <bits/stdc++.h>
  10. #include <random>
  11. #include <algorithm> // for copy
  12. #include <iterator> // for ostream_iterator
  13.  
  14.  
  15. using namespace std;
  16. //переменная с флагом остановки работы, чтобы останавливаться по Ctrl C
  17. bool stop = false;
  18. //обработчик Ctrl C
  19. BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
  20. switch (fdwCtrlType)
  21. {
  22. case CTRL_C_EVENT:
  23. //сигнал, применяемый в POSIX-системах для остановки процесса пользователем с терминала
  24. cout << "SIGINT signal received!" << endl;
  25. cout << "Stopping generator and all devices" << endl;
  26. cout.flush();
  27. stop = true;
  28. return TRUE;
  29. default:
  30. return FALSE;
  31. }
  32. }
  33.  
  34. //заявочка
  35. struct Request {
  36. int requestClass, type;
  37. };
  38.  
  39.  
  40. unsigned int RandomBetween(const int nMin, const int nMax)
  41. {
  42. std::random_device seeder;
  43. //then make a mersenne twister engine
  44. std::mt19937 engine(seeder());
  45. //then the easy part... the distribution
  46. std::uniform_int_distribution<int> dist(nMin, nMax);
  47. //then just generate the integer like this:
  48. return dist(engine);
  49. }
  50.  
  51.  
  52. void clear_screen (void)
  53. {
  54. DWORD n; /* Number of characters written */
  55. DWORD size; /* number of visible characters */
  56. COORD coord = {0}; /* Top left screen position */
  57. CONSOLE_SCREEN_BUFFER_INFO csbi;
  58.  
  59. /* Get a handle to the console */
  60. HANDLE h = GetStdHandle ( STD_OUTPUT_HANDLE );
  61.  
  62. GetConsoleScreenBufferInfo ( h, &csbi );
  63.  
  64. /* Find the number of characters to overwrite */
  65. size = csbi.dwSize.X * csbi.dwSize.Y;
  66.  
  67. /* Overwrite the screen buffer with whitespace */
  68. FillConsoleOutputCharacter ( h, TEXT ( ' ' ), size, coord, &n );
  69. GetConsoleScreenBufferInfo ( h, &csbi );
  70. FillConsoleOutputAttribute ( h, csbi.wAttributes, size, coord, &n );
  71.  
  72. /* Reset the cursor to the top left position */
  73. SetConsoleCursorPosition ( h, coord );
  74. }
  75.  
  76. //немного вспомогательной информации выводится в консоль, если данные были введены неправильно
  77. void printHelp(const std::string& name = ""){
  78. //печатаем информацию как пользоваться программой
  79. string help = "Usage: \n"
  80. "\t" + name + " queue_length groups_num group_capacity\n"
  81. "EVERY argument is required\n\n"
  82. "\tqueue_length > 0\n"
  83. "\tgroups_num, group_capacity >= 2\n"
  84. "add \"debug\" flag to print queue\n";
  85. cout << help << endl;
  86. }
  87.  
  88. //работа с очередью
  89. void Enqueue(vector<Request> &queue, Request &request) {
  90. if (queue.empty()) //если очередь пуста, просто добавляем в вектор заявку
  91. queue.push_back(request); //формат заявки выше
  92. else { //иначе ищем линейным поиском, где начинается подмножество заявок текущего типа
  93. for (int index = 0; index < queue.size(); index++){
  94. if (queue[index].type < request.type) {
  95. queue.insert(queue.begin() + index, request); //и вставляем заявку в начало
  96. break;
  97. }
  98. }
  99. }
  100. }
  101.  
  102. //приборы
  103. void Device(int groupNumber, int threadId, vector<Request> &queue, mutex &queueMutex, vector<string> &status) {
  104. //флаг для определения занятости
  105. bool isBusy = false; //сразу после создания прибор ещё не выполняет заявку
  106. while (!stop) { //пока не нажали Ctrl C цикл выполняется
  107. isBusy = false;
  108. int type;
  109. string threadStatus;
  110. queueMutex.lock(); //блокируем очередь на время поиска заявок, чтобы ничего лишнего не случилось
  111. if (!queue.empty()) { //и проверяем не пуста ли очередь
  112. for (int i = 0; i < queue.size() && !isBusy; i++) { //последовательный поиск по очереди заявок
  113. // если нашлась заявка нужного типа - берём её и обрабатываем
  114. if (queue[i].requestClass == groupNumber) {
  115. type = queue[i].type; //для отчёта о статусе сохраняем тип (приоритет) найденной заявки
  116. queue.erase(queue.begin() + i); //удаляем заявку из очереди
  117. isBusy = true; //далее прибор выполняет заявку
  118. }
  119. }
  120. }
  121. queueMutex.unlock(); //разблокируем очередь
  122. //отчитываемся о статусе
  123. int sleepTime = RandomBetween(64, 1024);
  124. threadStatus = "Device thread " + to_string(threadId);
  125. threadStatus += isBusy ? " is working on task of type " + to_string(type) + " for " + to_string(sleepTime) + " ms"
  126. : " is vacant"; //тернарка
  127. status[threadId] = threadStatus; //обновили статус прибора в векторе
  128. this_thread::sleep_for(chrono::milliseconds(sleepTime)); // поток прибора засыпает на случайное время
  129. // После окончания этого времени, прибор считается свободным (в начале цикла)
  130. }
  131. }
  132.  
  133. //функция подготавливает заявки случайных типов для случайных классов
  134. Request prepareRequest(int groupsCount){
  135. return Request {
  136. .requestClass = static_cast<int>(RandomBetween(0, groupsCount - 1)),
  137. .type = static_cast<int>(RandomBetween(1, 3)),
  138. };
  139. }
  140.  
  141.  
  142. void generator(int groupsCount, vector<Request> &queue, int &capacity, mutex &queueMutex, vector<string> &status) {
  143. while (!stop) {
  144. string generatorStatus;
  145. queueMutex.lock();//блокируем очередь на время проверок и добавления заявок
  146. //генератор через случайные промежутки времени добавляет в очередь заявки из разных групп со случайным типов
  147. if (queue.size() < capacity) {
  148. Request request = prepareRequest(groupsCount); //подготовка заявки
  149. Enqueue(queue, request); //подготовленную заявку помещаем в очередь
  150. generatorStatus = to_string(queue.size()) + " request(s) in queue";
  151. } //генератор неактивен, если в очереди нет свободных мест
  152. queueMutex.unlock(); //разблокируем очередь
  153. //обновляем статус генератора в последнем элементе вектора со статусами
  154. status[status.size() - 1] = generatorStatus;
  155. this_thread::sleep_for(chrono::milliseconds(RandomBetween(16, 128)));
  156.  
  157. }
  158. }
  159.  
  160.  
  161. void printQueue(mutex &queueMutex, const vector<Request> &queue) {
  162. cout << "\nQueue content:" << endl;
  163. queueMutex.lock(); //если этого не делать, будет гонка и будем читать освобождённую память
  164. for(vector<int>::size_type i = 0; i != queue.size(); i++) {
  165. cout << "Request " << i << " is type: " << queue[i].type << "; class: " << queue[i].requestClass << endl;
  166. }
  167. cout.flush();
  168. queueMutex.unlock();
  169. }
  170.  
  171.  
  172. void printStatus(int groupsCount, int deviceCount, const vector<string> &status) {
  173. for (int i = 0; i < groupsCount * deviceCount + 1; i++) {
  174. cout << status[i] << endl;
  175. }
  176. cout.flush();
  177. }
  178.  
  179. int main(int argc, char *argv[]) {
  180. srand(time(0));
  181. //было на русском, но CLion не может в русский, зато в дебаге может
  182. setlocale(LC_ALL, "rus");
  183. //объявляем три параметра
  184. int capacity, groupsCount, deviceCount;
  185. //считываем из аргументов параметры для программы, красевое
  186. capacity = argc > 1 ? atoi(argv[1]) : -1;
  187. groupsCount = argc > 2 ? atoi(argv[2]) : -1;
  188. deviceCount = argc > 3 ? atoi(argv[3]) : -1;
  189. //нужно если задан формат '10 5 5 debug'
  190. bool DEBUG = argc > 4 && std::string(argv[4]) == "debug";
  191. //если что-то ввели не так, или не ввели, выводим сообщение и выходим
  192. if (capacity <= 0 || groupsCount < 2 || deviceCount < 2){
  193. printHelp("MultiThreading.exe");
  194. return 1;
  195. }
  196.  
  197. mutex queueMutex; //мьютекс
  198. vector<Request> queue; // очередь-"накопитель"
  199. vector<string> status(groupsCount * deviceCount + 1, ""); //тексты статусов потоков
  200. //регистрируем обработчик Ctrl C, если не удалось - выходим
  201. if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
  202. return -1;
  203. }
  204. // создаём вектор для потоков устройств
  205. vector<thread> deviceThread(groupsCount * deviceCount);
  206. for (int i = 0; i < groupsCount * deviceCount; i++) {
  207. //и создаём потоки устройств
  208. //ref - это ссылка (для меня напоминание)
  209. deviceThread[i] = thread(Device, (int) i / deviceCount, i, ref(queue), ref(queueMutex), ref(status));
  210. }
  211. //создаём генератор
  212. thread gen(generator, ref(groupsCount), ref(queue), ref(capacity), ref(queueMutex), ref(status));
  213. //пока не нажали Ctrl C, в цикле выводим текущий статус
  214. while (!stop) {
  215. clear_screen();
  216. printStatus(groupsCount, deviceCount, status);
  217. if (DEBUG) printQueue(queueMutex, queue);
  218. this_thread::sleep_for(chrono::milliseconds(200));
  219. }
  220. //если попали сюда, значит, было нажато Ctrl C. Останавливаем генератор и потоки
  221. gen.join();
  222. cout << "Generator thread stopped" << endl;
  223. for (int i = 0; i < groupsCount * deviceCount; i++) {
  224. deviceThread[i].join();
  225. cout << "Device " << i << " thread stopped" << endl;
  226. }
  227. return 0;
  228. }
  229.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement