ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ Государственное образовательное учреждение высшего профессионального образования «ТОМСКИЙ ПОЛИТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ» Факультет автоматики и вычислительной техники Кафедра ВТ Отчет по лабораторной работе №3 по дисциплине «Операционные системы» Знакомство с подсистемой управления потоками в операционной системе Unix Выполнил: студент гр. 8В53 Сидоров А. О. Проверил: доцент каф. ВТ Шерстнев В.С. Томск 2008 Цель работы Ознакомиться с подсистемой управления потоками в операционной системе Unix и основными программными средствами для создания, управления и удаления потоков. Ход работы 1. Ознакомился с теоретическим материалом 2. Разработал многопоточные программы 2.1. Разработал программу с использованием двух потоков, один из которых записывает в файл положительные цифры, а другой - отрицательные. 2.1.1. Реализовал программу без синхронизации потоков Исходные код: #include <pthread.h> #include <fstream.h> ofstream output; void* WriteToFile(void *arg); int main() { char* filename = "output.txt"; output.open(filename); pthread_t threadA, threadB; pthread_create(&threadA, NULL, &WriteToFile, (void*)-5); pthread_create(&threadB, NULL, &WriteToFile, (void*)5); pthread_join(threadA, NULL); pthread_join(threadB, NULL); output.close(); } void* WriteToFile(void *arg) { int max = (int)arg; if (max > 0) for (int i = 0; i < max; i++) { output << i << '\n'; sleep(1); } else for (int i = max; i < 0; i++) { output << i << '\n'; sleep(1); } } Скомпилировал и запустил прорамму: Просмотрел вывод программы: Ни первый, ни второй поток не успевают выполнить задачу полностью за один квант времени, в результате чего в файле output.txt смешиваются результаты работы первого и второго потоков. 2.1.2. Реализовал эту же программу, синхронизировав потоки при помощи мьютексов Исходный код изменился несущественно: была добавлена глобальная переменная типа pthread_mutex_t, инициализация этой переменной в методе main и непосредственно блокировка критической секции посредством мьютекса: #include <pthread.h> #include <fstream.h> ofstream output; pthread_mutex_t outputMutexLock; void* WriteToFile(void *arg); int main() { char* filename = "output.txt"; output.open(filename); pthread_mutex_init(&outputMutexLock, NULL); pthread_t threadA, threadB; pthread_create(&threadA, NULL, &WriteToFile, (void*)-5); pthread_create(&threadB, NULL, &WriteToFile, (void*)5); pthread_join(threadA, NULL); pthread_join(threadB, NULL); pthread_mutex_destroy(&outputMutexLock); output.close(); } void* WriteToFile(void *arg) { int max = (int)arg; pthread_mutex_lock(&outputMutexLock); if (max > 0) for (int i = 0; i < max; i++) { output << i << '\n'; sleep(1); } else for (int i = max; i < 0; i++) { output << i << '\n'; sleep(1); } pthread_mutex_unlock(&outputMutexLock); } Скомпилировал и выполнил программу: Просмотрел вывод программы: В результате синхронизации потоков выполнение кода, заключенного в мьютекс, не прерывается. На этот раз в выходном файле отдельно расположены результаты работы обоих потоков. 2.2. Разработал многопоточную программу, эмулирующую рынок (один поток создает, другой – потребляет), синхронизировав потоки посредством условных переменных Исходный код: #include <pthread.h> #include <fstream.h> #include <iostream.h> #include <stdlib.h> #include <time.h> void* ProduceProduct(void *arg); void* ConsumeProduct(void *arg); int quantityOfProduct; pthread_mutex_t mutex; pthread_cond_t accessibilityCondition; int main() { srand(time(NULL)); pthread_mutex_init(&mutex, 0); pthread_cond_init(&accessibilityCondition, 0); pthread_t producingThread; pthread_create(&producingThread, NULL, ProduceProduct, NULL); pthread_t consumingThread; pthread_create(&consumingThread, NULL, ConsumeProduct, NULL); pthread_join(producingThread, NULL); pthread_cancel(consumingThread); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&accessibilityCondition); } void* ProduceProduct(void *arg) { for (int i = 0; i < 10; i++) { pthread_mutex_lock(&mutex); int produced = rand() % 10 + 1; quantityOfProduct += produced; sleep(rand() % 10 + 1); cout << "Produced: " << produced << " pthread_mutex_unlock(&mutex); TotalQuantity: " << quantityOfProduct << '\n'; pthread_cond_signal(&accessibilityCondition); } } void* ConsumeProduct(void *arg) { while (true) { pthread_mutex_lock(&mutex); int requiredQuantity = rand() % 10 + 1; while (quantityOfProduct < requiredQuantity) pthread_cond_wait(&accessibilityCondition, &mutex); quantityOfProduct -= requiredQuantity; cout << "Consumed: " << requiredQuantity << " TotalQuantity: " << quantityOfProduct << '\n'; pthread_mutex_unlock(&mutex); } } Скомпилировал и выполнил программу: Скриншот иллюстрирует взаимодействие потоков: 1. если на «складе» есть достаточное количество продукции, потребить «забирает» его, 2. если же на «складе» продукции меньше, чем требуется потребителю, поток потребления ждет «сигнала» от потока производства. 2.3. Разработал многопоточную программу, эмулирующую при помощи семафора работу пула (например, модемного пула провайдера) Исходный код: #include <semaphore.h> #include <pthread.h> #include <stdlib.h> #include <time.h> #include <iostream.h> #define n 10 void* GetContent(void *arg); sem_t connectionSemaphore; pthread_mutex_t mutex; int main() { time_t seconds; time(&seconds); srand((unsigned)seconds); sem_init(&connectionSemaphore, 0, 10); pthread_mutex_init(&mutex, 0); pthread_t threads[n]; for (int i = 0; i < n; i++) pthread_create(&threads[i], NULL, GetContent, (void*)i); for (int i = 0; i < n; i++) pthread_join(threads[i], NULL); sem_destroy(&connectionSemaphore); pthread_mutex_destroy(&mutex); } void* GetContent(void *arg) { int threadId = (int)arg; sem_wait(&connectionSemaphore); pthread_mutex_lock(&mutex); cout << "Поток # " << threadId << " подключился" << '\n'; pthread_mutex_unlock(&mutex); pthread_mutex_lock(&mutex); cout << "Поток #" << threadId << " получил данные" << '\n'; pthread_mutex_unlock(&mutex); sem_post(&connectionSemaphore); } Скомпилировал и выполнил программу: Суть работы пула заключается в том, что определено фиксированное число одновременных «подключений» (в данном случае 5), а «лишние» «подключения» ставятся в очередь. Скриншот не иллюстрирует в полной мере работу программы (на самом деле поток 5 подключается сразу после того, как поток 0 получил данные) из-за того, что помимо очереди семафора потоки выстраиваются и в очередь мьютекса, блокирующего вывод. Выводы: В ходе выполнения лабораторной работы были изучены средства для создания, удаления и синхронизации потоков. Синхронизация потоков может осуществляться посредством семафоров, мьютексов и условных переменных. Семафор позволяет ограничить доступ к ресурсу определенному числу потоков. Мьютекс является одноместным семафором. Условные же переменные позволяют синхронизировать потоки, каким бы сложным не было их взаимодействие.