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

Эти эффекты не связаны с какой-то конкретной формой вывода, такой как вывод в поток, показанный выше; точно так же будет вести себя и традиционный вызов printf(...).

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

const int N = ... , M = ...;

char buf[N];

volatile unsigned ind = 0;

...

// а вот это производится из каждого потока

char tbuf[M];

sprintf(tbuf, "Это вывод потока %X", pthread_self());

strcpy(buf + atomic_add_value(ind, strlen(tbuf)), tbuf);

И наконец, последнее: есть ли смысл во введении этого дополнительного механизма, если всегда существует альтернативная форма организации такой же защиты доступа посредством критической секции (например, при использовании мьютекса)? Сравним ( файл a1.cc) временные затраты при многократном изменении значения переменной для случаев атомарных операций и критической секции на базе мьютекса (мы берем именно мьютекс, потому что из всех примитивов синхронизации он самый низкоуровневый и быстрый):

Сравнение мьютекса и двух форм вызова атомарной операции

#include

#include

#include

#include

#include

#include

#include

#include

int main(int argc, char *argv[]) {

 uint64_t N = 100000;

 bool atom = false, value = false;

 int opt, val;

 while ((opt = getopt(argc, argv, "n:av")) != -1) {

  switch(opt) {

  case 'n': // количество повторений

   if (sscanf(optarg, "%i", &val) != 1)

    cout << "parse command line error" << endl, exit(EXIT_FAILURE);

   if (val > 0) N = val;

   break;

  // использовать атомарные операции

  case 'a':

   atom = true;

   break;

  // использовать форму, возвращающую значение

  case 'v':

   value = true;

   break;

  default:

   exit(EXIT_FAILURE);

  }

 }

 // замеряется количество процессорных циклов для каждого случая

 uint64_t i = N, t = ClockCycles();

 volatile unsigned ind = 0;

 if (!atom) {

  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  while (i--) {

   pthread_mutex_lock(&mutex);

   ind++;

   pthread_mutex_unlock(&mutex);

  }

 } else if (value)

  while (i--) atomic_add_value(&ind, 1);

 else while (i--) atomic_add(&ind, 1);

 t = ClockCycles() - t;

 cout << "all cycles - " << t << "; on operation - "

  << t / N << endl;

 exit(EXIT_SUCCESS);

}

Вот результат при использовании критической секции:

# nice -n-19 a1 -n10000000

all cycles - 1120872156; on operation - 112

Результат с применением атомарной операции, не возвращающей значения:

# nice -n-19 a1 -n10000000 -a

all cycles — 391018203; on operation - 39

Результат с применением атомарной операции, возвращающей значение (обещанная разница составляет порядка 10%):

# nice -n-19 a1 -n10000000 -a -v

all cycles - 441158981; on operation - 44

<p>Условная переменная</p>

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

Реализация подобного ожидания «в лоб» привела бы к тому, что все потоки, разделяющие данную критическую секцию, были бы вынуждены ждать выполнения условия для каждого из них. При «правильной» реализации ожидания поток должен освобождать мьютекс на время ожидания и вновь захватывать его, когда ожидаемое условие выполняется. Специально для этого случая стандартом POSIX предусмотрены условные переменные. QNX Neutrino реализует условные переменные как на уровне вызовов микроядра в своем native API, так и в соответствии со стандартом POSIX.

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

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

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

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

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

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

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

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

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