Читаем QNX/UNIX: Анатомия параллелизма полностью

Две формы атомарных функций для каждой операции отличаются тем, что первая из них выполняет операцию без возврата значения, а вторая возвращает значение, которое операнд Dимел до выполнения операции (т.e. прежнее значение, как это делают, например, префиксные операции инкремента ++Dи декремента --D, в отличие от постфиксных D++и D--).

Зачем нужны две формы для операции? Техническая документация QNX утверждает, что вторая форма может выполняться дольше. Справедливость этого утверждения и насколько дольше выполняется вторая форма, мы скоро увидим на примерах.

Итак, у нас есть 10 функций для выполнения пяти атомарных операций:

atomic_add()    atomic_add_value()

atomic_sub()    atomic_sub_value()

atomic_clr()    atomic_clr_value()

atomic_set()    atomic_set_value()

atomic_toggle() atomic_toggle_value()

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

// глобальные описания, доступные всем потокам

const unsigned int N = ...

uint8_t buf[N];

// индекс текущей позиции записи

unsigned int ind = 0;

// общий мьютекс, доступный каждому из потоков

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

...

// выполняется в каждом из потоков:

uint8_t res[M]; // результат некоторой операции

unsigned int how = ... // реальная длина этого результата

pthread_mutex_lock(&mutex);

memcpy((void*)buf + ind, (void*)res, how);

ind += how;

pthread_mutex_unlock(&mutex);

Используя атомарные операции, мы можем этот процесс записать так (все глобальные описания остаются неизменными):

// глобальные описания, доступные всем потокам

...

// индекс текущей позиции записи

volatile unsigned int ind = 0;

...

// выполняется в каждом из потоков:

uint8_t res[M]; // результат некоторой операции

unsigned int how = ... // реальная длина этого результата

memcpy((void*)buf + atomic_add_value(ind, how), (void*)res, how);

Или даже так:

// глобальные описания, доступные всем потокам

...

// указательтекущей позиции записи:

volatile unsigned int ind = (unsigned int)buf;

...

// выполняется в каждом из потоков:

memcpy((void*)atomic_add_value(ind, how), (void*)res, how);

В последнем случае это, конечно, трюкачество, построенное на том, что в 32-разрядной архитектуре представления указателя и переменной unsigned intсовпадают, но это «работающее трюкачество» и работающее иногда весьма эффективно.

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

cout << "Это вывод потока " << pthread_self() << endl;

Но так делать нельзя: при таком решении у нас появляются 2 проблемы, одна из которых очевидна, а другая — не очень:

1. Выводы разных потоков могут «смешиваться»; более того, за счет буферизации вывода некоторые «рваные» фрагменты мы будем наблюдать дважды. Одним словом, наш вывод окажется полностью «нечитабельным».

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

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных