Олег Цилюрик - QNX/UNIX: Анатомия параллелизма
- Название:QNX/UNIX: Анатомия параллелизма
- Автор:
- Жанр:
- Издательство:Символ-Плюс
- Год:2006
- Город:Санкт-Петербург
- ISBN:5-93286-088-Х
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Олег Цилюрик - QNX/UNIX: Анатомия параллелизма краткое содержание
Книга адресована программистам, работающим в самых разнообразных ОС UNIX. Авторы предлагают шире взглянуть на возможности параллельной организации вычислительного процесса в традиционном программировании. Особый акцент делается на потоках (threads), а именно на тех возможностях и сложностях, которые были привнесены в технику параллельных вычислений этой относительно новой парадигмой программирования. На примерах реальных кодов показываются приемы и преимущества параллельной организации вычислительного процесса. Некоторые из результатов испытаний тестовых примеров будут большим сюрпризом даже для самых бывалых программистов. Тем не менее излагаемые техники вполне доступны и начинающим программистам: для изучения материала требуется базовое знание языка программирования C/C++ и некоторое понимание «устройства» современных многозадачных ОС UNIX.
В качестве «испытательной площадки» для тестовых фрагментов выбрана ОСРВ QNX, что позволило с единой точки зрения взглянуть как на специфические механизмы микроядерной архитектуры QNX, так и на универсальные механизмы POSIX. В этом качестве книга может быть интересна и тем, кто не использует (и не планирует никогда использовать) ОС QNX: программистам в Linux, FreeBSD, NetBSD, Solaris и других традиционных ОС UNIX.
QNX/UNIX: Анатомия параллелизма - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
EDEADLK
— вызывающий поток уже является владельцем указанного объекта синхронизации; он захватил его, используя блокировку по записи, и повторная блокировка по чтению привела бы к полному блокированию потока;
EINVAL
— неверный параметр вызова: либо rwl
указывает на неинициализированный объект блокировки чтения/записи, либо время тайм-аута abs
задано меньше нуля или равно или выше предельного значения 1000 миллионов;
ETIMEDOUT
— не удалось захватить блокировку до истечения срока тайм-аута.
int pthread_rwlock_wrlock(pthread_rwlock_t* rwl);
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwl);
int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock,
const struct timespec* abs_timeout);
Функции этой группы предназначены для эксклюзивного захвата объекта синхронизации и использования его для блокирования по записи. Блокировка по записи, в отличие от блокировки по чтению, не допускает совместного исполнения защищаемого участка кода (ни пишущими, ни читающими потоками). Так же как в группе функций блокировки по чтению, в этой группе присутствуют функции простого захвата, попытки захвата и захвата с тайм-аутом ожидания освобождения.
Функция pthread_rwlock_wrlock()
возвращает следующие значения:
EOK
— успешное выполнение;
EAGAIN
— при первом использовании статически инициированной блокировки чтения/записи ( PTHREAD_RWLOCK_INITIALIZER
) недостаточно системных ресурсов для инициализации блокировки чтения/записи;
EDEADLK
— вызывающий поток уже является владельцем блокировки в эксклюзивном режиме;
EFAULT
— сбой при обращении ядра к rwl
;
EINVAL
— rwl
указывает на неверный объект блокировки чтения/записи.
Функция pthread_rwlock_trywrlock()
возвращает значения:
EOK
— успешное выполнение;
EAGAIN
— при первом использовании статически инициированной блокировки чтения/записи ( PTHREAD_RWLOCK_INITIALIZER
) недостаточно системных ресурсов для инициализации блокировки чтения/записи;
EBUSY
— блокировка уже захвачена в режиме чтения или записи;
EDEADLK
— вызывающий поток уже является владельцем блокировки в эксклюзивном режиме;
EFAULT
— сбой при обращении ядра к rwl
;
EINVAL
— rwl
указывает на неверный объект блокировки чтения/записи.
Функция pthread_rwlock_timedwrlock()
возвращает значения:
EOK
— успешное выполнение;
EAGAIN
— система не может захватить блокировку по записи, поскольку достигнуто максимальное число блокировок по записи для данного объекта;
EDEADLK
— вызывающий поток уже является владельцем блокировки в эксклюзивном режиме;
EINVAL
— неверный параметр вызова: либо rwl
указывает на неинициализированный объект блокировки чтения/записи, либо время тайм-аута abs задано меньше нуля или равно или выше предельного значения 1000 миллионов;
ETIMEDOUT
— не удалось захватить блокировку до истечения заданного срока тайм-аута.
Освобождение блокировки
int pthread_rwlock_unlock(pthread_rwlock_t* rwl);
Функция освобождает захваченный любым образом объект блокировки чтения/записи. Если объект был захвачен в режиме множественного использования (блокировки по чтению), то количество его освобождений должно равняться количеству захватов.
Возвращаемые значения:
EOK
— успешное завершение;
EAGAIN
— при первом использовании статически инициированной блокировки чтения/записи ( PTHREAD_RWLOCK_INITIALIZER
) недостаточно системных ресурсов для инициализации блокировки чтения/записи;
EFAULT
— ядро не смогло обратиться к объекту rwl
;
EINVAL
— объект rwl
указывает на неверно инициированный объект блокировки чтения/записи;
EPERM
— нет потоков, захвативших объект rwl
в режиме чтения или записи, или вызывающий поток не владеет блокировкой в режиме записи.
Использование блокировок чтения/записи
Построим приложение, использующее блокировку чтения/записи ( файл sy10.cc ):
#include
#include
#include
// сколь угодно сложные элементы внутренней базы данных
// приложения; в примере мы используем их простейший вид
typedef int element;
// база данных приложения - динамический список элементов
class dbase : public list {
static const int READ_DELAY = 1, WRITE_DELAY = 2;
public:
// операция "добавить в базу данных"
void add(const element& e) {
int pos = size() * rand() / RAND_MAX;
list::iterator p = begin();
for (int i = 0; i < pos; i++) p++;
insert(p, e);
delay(WRITE_DELAY);
}
// операция "найти в базе данных"
int pos(const element& e) {
int n = 0;
for (list::iterator i = begin(); i != end(); i++, n++)
if (*i == e) {
delay(READ_DELAY);
break;
}
if (n == size()) n = -1;
return n;
}
} data;
inline element erand(unsigned long n) {
return (element)((n * rand()) / RAND_MAX);
}
inline bool wrand(double p) {
return (double)rand() / (double)RAND_MAX < p;
}
int main(int argc, char *argv[]) {
// общее число обращений приложения к базе данных
static unsigned long n = 1000;
// вероятность обновления базы данных при обращении
static double p = .1;
unsigned long m;
if (argc > 1 && (m = atoll(argv[1])) ! = 0) n = m;
double q;
if (argc > 2 && (q = atof(argv[2])) != 0) p = q;
// начальное заполнение базы данных
for (int i = 0; i < n; i++) data.add(erand(n));
// тактовая частота процессора (для измерения времени)
const uint64_t cps = SYSPAGE_ENTRY(qtime)->cycles_per_sec;
// последовательность n обращений к базе данных,
// из них p*n требуют обновления списка, а остальные
// (1-p)*n требуют только выборки данных:
uint64_t t = ClockCycles();
for (int i = 0; i < n; i++) {
element e = erand(n);
if (!wrand(p)) data.pos(e);
else data.add(e);
}
t = ((ClockCycles() - t) * 1000000000) / cps;
cout << "evaluation time: " << (double)t / 1.E9
<< " sec." << endl;
return EXIT_SUCCESS;
}
Перед нами простейшая последовательная программа, которая массированно читает свою базу данных и изредка ее модифицирует. Для выполнения реальных операций чтения/записи данных программе необходимо выполнять некоторые достаточно продолжительные операции. В приведенном коде эти операции имитируются задержками delay(WRITE_DELAY)
и delay(READ_DELAY)
.
Возникает совершенно естественное желание преобразовать последовательные запросы к данным в параллельные ( файл sy11.cc ). Для этого преобразуем структуру списка элементов, с которым работаем:
class dbase : public list {
static const int READ_DELAY = 1, WRITE_DELAY = 2;
pthread_mutex_t loc;
public:
Интервал:
Закладка: