Алгоритм решения аналогичен алгоритму решения на основе монитора1. (Недостатком является отсутствие решения проблемы голодания: один из философов (или несколько) может долго ждать своих соседей.)
Запустим программу на выполнение и проверим её работу (рис. 5).
Рисунок 5 — Работа алгоритма решения задачи об обедающих философах В данном случае мы использовали C++, а не Си, так как задача лежит
ближе к ООП, нежели к чисто процедурной парадигме. В соответствии с выбранной парадигмой и языком, мы выбрали класс thread библиотеки поддержки потоков языка C++. Методы и их описания приведены в табл. 3.
Таблица 3 — Методы класса thread (#include <thread>)
bool joinable() const noexcept; |
Проверяет, можно ли вызвать метод join; |
|
|
std::thread::id get_id() const |
Возвращает идентификатор потока; |
noexcept; |
|
native_handle_type |
Возвращает базовый дескриптор потока, |
native_handle(); |
определяемый реализацией; |
1https://ru.wikipedia.org/wiki/Задача_об_обедающих_философах#Решение_на_основе_монитора
6
static unsigned int |
Возвращает количество параллельных |
|
hardware_concurrency() |
||
потоков, поддерживаемых реализацией; |
||
noexcept; |
||
|
|
|
void join(); |
Ждет, пока поток закончит свое |
|
выполнение; |
||
|
||
|
Отделяет поток от объекта thread, позволяя |
|
void detach(); |
потоку продолжить выполнение |
|
|
независимо; |
|
void swap(std::thread& other) |
Меняет местами два объекта потока. |
|
noexcept; |
||
По желанию можно переписать эту программу с использованием |
||
POSIX функций (табл. 4). |
|
|
Таблица 4 — Функции POSIX для работы с потоками (#include <pthread.h>)
int pthread_create(pthread_t |
Создает поток, идентификатор созданного |
*restrict thread, const |
|
pthread_attr_t *restrict attr, |
потока возвращается в качестве выходного |
void *(*start_routine)(void*), |
параметра; |
void *restrict arg); |
|
pthread_t pthread_self(void); |
Получает идентификатор текущего потока; |
Сравнивает два идентификатора потоков
(может использоваться, к примеру, для int pthread_equal(pthread_t t1, сравнения некоторого идентификатора
pthread_t t2);
потока со значением, возвращаемым
функцией pthread_self);
int pthread_join(pthread_t |
Ожидает завершения некоторого потока и |
|
освобождает ресурсы, выделенные при его |
||
thread, void **status); |
||
|
создании; |
|
int pthread_detach(pthread_t |
Отделяет поток от дескриптора thread, |
|
позволяя потоку продолжить выполнение |
||
thread); |
||
|
независимо; |
|
void pthread_exit(void |
Завершает текущий поток; |
|
*value_ptr); |
||
int pthread_cancel(pthread_t |
Запрашивает принудительное завершение |
|
target_thread); |
другого потока. |
Важно не путать POSIX функции и функции языка Си (табл. 5) для работы с потоками.
Таблица 5 — Функции языка C для работы с потоками (#include <threads.h>)
int thrd_create(thrd_t *thr, |
Создает поток; |
thrd_start_t func, void *arg); |
|
int thrd_equal(thrd_t lhs, |
Проверяет, относятся ли два |
thrd_t rhs); |
идентификатора к одному и тому же потоку; |
thrd_t thrd_current(void); |
Получает идентификатор текущего потока; |
|
|
int thrd_sleep(const struct |
Приостанавливает выполнение |
timespec* duration, struct |
|
7
timespec* remaining); |
|
вызывающего потока на заданный период |
|
|
времени; |
||
|
|
||
|
|
|
|
void thrd_yield(void); |
|
Дает подсказку реализации об ожидании; |
|
|
|
|
|
_Noreturn void thrd_exit(int |
|
Завершает вызывающий поток; |
|
res); |
|
||
int thrd_detach(thrd_t thr); |
|
Отделяет поток от дескриптора thr; |
|
|
|
|
|
int thrd_join(thrd_t thr, int |
|
Блокируется, пока поток не завершится. |
|
*res); |
|
||
Заключение В результате выполнения лабораторной работы мы ознакомились с
применением механизмов синхронизации потоков под Linux.
8