Материал: metod_ukaz

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Структура буфера

Буфер представляет собой массив из N элементов определенного типа. Состояние буфера описывается количеством сообщений n, находящихся в буфере, и двумя индексами - индексом out чтения и индексом in записи.

Запись в буфер предваряется проверкой условия «буфер полон», т. е. (n == N), а чтение из буфера - проверкой условия «буфер пуст», т. е. (n == 0).

Выполнение условия «буфер полон» означает, что скорость записи превысила скорость чтения, а выполнение условия «буфер пуст» означает, что скорость чтения выше скорости записи. В нормальном состоянии значение индекса записи немного превышает значение индекса чтения, что иллюстрируется следующим рисунком:

-----------------------

| |

-----------------------

|/////////////////////| ---------> out чтение

-----------------------

|/////////////////////| (Следующее чтение)

-----------------------

|/////////////////////|

-----------------------

Запись in ------>| |

-----------------------

(Следующая запись) | |

-----------------------

Обычно буфер реализуется как кольцевой, т. е. после записи в последнюю ячейку буфера запись продолжается с первой ячейки, а после чтения из последней ячейки чтение продолжается с первой ячейки.

Описание буфера

Описание буфера содержит несколько переменных и несколько функций:

int in; //индекс записи

int out; //индекс чтения

int n; //количество элементов в буфере

char Buf[N];//буфер, N – константа, размер буфера

void buffer_init()//инициализация буфера

{

in = 0;

out = 0;

n = 0;

}

void Write(char M)//запись данных в буфер

{

вход в критический участок;

while (n == N){//буфер полный

перейти к ожиданию записи с одновременным освобождением критического участка;

}

n++;

Buf[in] = M;

in = (in + 1) % N;

сигнализировать о возможности чтения;

выход из критического участка;

}

char Read()//чтение данных из буфера

{

вход в критический участок;

while (n == 0) {//буфер пустой

перейти к ожиданию чтения с одновременным освобождением критического участка;

}

n--;

char М = Buf[out];

out = (out + 1) % N;

сигнализировать о возможности записи;

выход из критического участка;

return M;

}

Буфер (Buf) и текущее количество сообщений в буфере (n) являются критическим ресурсом, поскольку потоки записи и чтения могут одновременно писать и читать данные, а также проверять и устанавливать значение n. Поэтому операции записи в буфер и чтения из буфера должны выполняться в режиме взаимного исключения.

Для реализации взаимного исключения предназначен объект мьютекс.

Для реализации блокировки потока с одновременным освобождением мьютекса предназначен объект «условная переменная».

Условная переменная – это средство синхронизации, над которым выполняются следующие операции.

Создание условной переменной:

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

где:

pthread_cond_t *cond – указатель на «условную переменную» - переменную типа pthread_cond_t;

const pthread_condattr_t *attr – структура, описывающая атрибуты условной переменной.

Разрушение условной переменной:

int pthread_cond_destroy(pthread_cond_t *cond).

Ожидание на условной переменной:

Если поток вызывает операцию:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

где:

pthread_cond_t *cond – указатель на «условную переменную» - переменную типа pthread_cond_t;

pthread_mutex_t *mutex – указатель на мьютекс,

то поток блокируется и освобождает мьютекс, указанный в операции.

При этом блокировка и освобождение мьютекса выполняются как одно «атомарное» действие.

Сигнализирующая операция на условной переменной:

Если поток вызывает операцию:

int pthread_cond_signal(pthread_cond_t *cond);

то заблокированный на этой условной переменной поток продолжает свое выполнение с той точки программы, на которой он был заблокирован, при этом, с захваченным мьютексом.

Две приведенные операции могут быть выполнены с дополнительными возможностями.

  1. Если существует несколько потоков, заблокированных на условной переменной, то их можно одновременно активизировать, вызвав функцию:

int pthread_cond_broadcast(pthread_cond_t *cond).

Цикл while() для повторной проверки состояния буфера позволяет в результате «гонок» только одному из потоков продолжить выполнение в критическом участке. Остальные потоки будут повторно заблокированы.

  1. Если есть поток, заблокированный на условной переменной, но нет потока, который может его активизировать (например, поток аварийно завершился), то можно вместо блокировки на «бесконечное время» использовать блокировку на определенное время с помощью функции:

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

где:

pthread_cond_t *cond – указатель на «условную переменную»;

pthread_mutex_t *mutex – указатель на мьютекс,

const struct timespec *abstime – абсолютное время завершения ожидания. Если время ожидания истекло, а сигнал на активизацию не был получен, то функция возвращает ошибку [ETIMEDOUT].

Таким образом, описание буфера должно быть дополнено тремя элементами:

pthread_cond_t readCV – условная переменная для блокировки потока, ждущего чтения;

pthread_cond_t writeCV – условная переменная для блокировки потока, ждущего записи;

pthread_mutex_t mutex – мьютекс для обеспечения взаимного исключения при вызове операций записи в буфер и чтения из буфера.

Указания к выполнению работы

Реализовать «Буфер» в виде программного кода.

Запрограммировать задачу взаимодействия двух потоков с использованием созданной реализации «Буфера».

Проанализировать ситуации, когда скорость записи данных выше скорости чтения и когда скорость записи данных ниже скорости чтения.

Для получения возможности корректного завершения программы для блокировки потоков использовать функцию pthread_cond_timedwait().

Вопросы для самопроверки

  1. Дайте определение понятия «условная переменная».

  2. Какие действия выполняются над мьютексом, адрес которого передается в операцию ожидания условной переменной? Какова цель этих действий?

  3. Какие существуют варианты активизации потоков, блокированных на условной переменной?

  4. Как избежать проблем, связанных с блокировкой потока на бесконечное время, в случае отсутствия потоков, выполняющих сигнализирующую операцию?

  5. Как преодолевается опасность одновременного входа в критический участок нескольких потоков, разблокированных широковещательной сигнализирующей операцией?

  6. Какие высокоуровневые объекты синхронизации реализуются с помощью условных переменных?

  7. Какие атрибуты имеются у объекта – условная переменная?

Заключение

Работы выполняются индивидуально. Отчет представляется в электронном виде.

Отчет должен содержать описание средства взаимодействия процессов, изучаемого в данной лабораторной работе, подробное описание системных вызовов, используемых в программе, параметров, передаваемых в эти вызовы, текст работающей программы с содержательными комментариями и результаты работы программ.

Защита каждой работы включает в себя следующие этапы:

  1. Студент предъявляет отчет по лабораторной работе;

  2. Студент демонстрирует следующие умения:

      • умение запустить программу;

      • умение внести изменение в текст программы (требуемые изменения преподаватель фиксирует в отчете);

      • умение пересобрать программу и снова ее запустить.

  1. Студент отвечает на дополнительные вопросы.

73

Источник: https://studfile.net/preview/16676187/