Читаем UNIX: разработка сетевых приложений полностью

Подобные ошибки параллельного программирования трудно обнаружить по многим причинам. Во-первых, они возникают нечасто. Тем не менее это ошибки, которые по закону Мэрфи вызывают сбои в работе программ. Во-вторых, ошибки такого типа возникают не систематически, так как зависят от недетерминированного совпадения нескольких событий. Наконец, в некоторых системах аппаратные команды могут быть атомарными. Это значит, что имеется аппаратная команда уменьшения значения целого числа на единицу (вместо трехступенчатой последовательности, которую мы предположили выше), а аппаратная команда не может быть прервана до окончания своего выполнения. Но это не гарантировано для всех систем, так что код может работать в одной системе и не работать в другой.

Программирование с использованием потоков является параллельным (parallel), или одновременным (concurrent), программированием, так как несколько потоков могут выполняться параллельно (одновременно), получая доступ к одним и тем же переменным. Хотя ошибочный сценарий, рассмотренный нами далее, предполагает систему с одним центральным процессором, вероятность ошибки также присутствует, если потоки А и В выполняются в одно и то же время на разных процессорах в многопроцессорной системе. В обычном программировании под Unix мы не сталкиваемся с подобными ошибками, так как при использовании функции fork родительский и дочерний процессы не используют совместно ничего, кроме дескрипторов. Тем не менее мы столкнемся с ошибками этого типа при обсуждении совместного использовании памяти несколькими процессами.

Эту проблему можно с легкостью продемонстрировать на примере потоков. В листинге 26.11 показана программа, которая создает два потока, после чего каждый поток увеличивает некоторую глобальную переменную 5000 раз.

Мы повысили вероятность ошибки за счет того, что потребовали от программы получить текущее значение переменной counter, вывести это значение и записать его. Если мы запустим эту программу, то получим результат, представленный в листинге 26.10.

Листинг 26.10. Результат выполнения программы, приведенной в листинге 26.11

4: 1

4: 2

4: 3

4: 4

 продолжение выполнения потока номер 4

4: 517

4: 518

5: 518 теперь выполняется поток номер 5

5: 519

5: 520

 продолжение выполнения потока номер 5

5: 926

5: 927

4: 519 теперь выполняется поток номер 4, записывая неверные значения

4: 520

Листинг 26.11. Два потока, которые неверно увеличивают значение глобальной переменной

//threads/example01.c

 1 #include "unpthread.h"

 2 #define NLOOP 5000

 3 int counter; /* потоки должны увеличивать значение этой переменной */

 4 void *doit(void*);

 5 int

 6 main(int argc, char **argv)

 7 {

 8  pthread_t tidA, tidB;

 9  Pthread_create(&tidA, NULL, &doit, NULL);

10  Pthread_create(&tidB, NULL, &doit, NULL);

11  /* ожидание завершения обоих потоков */

12  Pthread_join(tidA, NULL);

13  Pthread_join(tidB, NULL);

14  exit(0);

15 }

16 void*

17 doit(void *vptr)

18 {

19  int i, val;

20  /* Каждый поток получает, выводит и увеличивает на

21   * единицу переменную counter NLOOP раз. Значение

22   * переменной должно увеличиваться монотонно.

23   */

24  for (i = 0; i < NLOOP; i++) {

25   val = counter;

26   printf("%d: %d\n", pthread_self(), val + 1);

27   counter = val + 1;

28  }

29  return (NULL);

30 }

Обратите внимание на то, что в первый раз ошибка происходит при переключении системы с выполнения потока номер 4 на выполнение потока номер 5: каждый поток в итоге записывает значение 518. Это происходит множество раз на протяжении 10 000 строк вывода.

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

Все книги серии Мастер-класс

Секреты резьбы по дереву
Секреты резьбы по дереву

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

Галина Алексеевна Серикова

Сделай сам / Хобби и ремесла / Руководства / Дом и досуг / Словари и Энциклопедии

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