Чтение онлайн

на главную

Жанры

UNIX: разработка сетевых приложений
Шрифт:

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

1. Выполняется

поток А, который загружает в регистр значение переменной
nconn
(равное 3).

2. Система переключается с выполнения потока А на выполнение потока В. Регистры потока А сохранены, регистры потока В восстановлены.

3. Поток В выполняет три действия, составляющие оператор декремента в языке С (

nconn--
), сохраняя новое значение переменной
nconn
, равное 2.

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

nconn
.

Окончательный результат таков: значение

nconn
равно 2, в то время как оно должно быть равным 1. Это ошибка.

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

Программирование с использованием потоков является параллельным( 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 }

Поделиться:
Популярные книги

Треск штанов

Ланцов Михаил Алексеевич
6. Сын Петра
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Треск штанов

Начальник милиции. Книга 3

Дамиров Рафаэль
3. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Начальник милиции. Книга 3

Ты нас предал

Безрукова Елена
1. Измены. Кантемировы
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Ты нас предал

Ледяное проклятье

Михайлов Дем Алексеевич
4. Изгой
Фантастика:
фэнтези
9.20
рейтинг книги
Ледяное проклятье

Месть за измену

Кофф Натализа
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Месть за измену

Мужчина не моей мечты

Ардова Алиса
1. Мужчина не моей мечты
Любовные романы:
любовно-фантастические романы
8.30
рейтинг книги
Мужчина не моей мечты

Его огонь горит для меня. Том 2

Муратова Ульяна
2. Мир Карастели
Фантастика:
юмористическая фантастика
5.40
рейтинг книги
Его огонь горит для меня. Том 2

Возвышение Меркурия. Книга 2

Кронос Александр
2. Меркурий
Фантастика:
фэнтези
5.00
рейтинг книги
Возвышение Меркурия. Книга 2

Камень. Книга шестая

Минин Станислав
6. Камень
Фантастика:
боевая фантастика
7.64
рейтинг книги
Камень. Книга шестая

Идеальный мир для Лекаря 2

Сапфир Олег
2. Лекарь
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 2

Восход. Солнцев. Книга VI

Скабер Артемий
6. Голос Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восход. Солнцев. Книга VI

Не грози Дубровскому! Том 11

Панарин Антон
11. РОС: Не грози Дубровскому!
Фантастика:
фэнтези
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Не грози Дубровскому! Том 11

Мама из другого мира. Делу - время, забавам - час

Рыжая Ехидна
2. Королевский приют имени графа Тадеуса Оберона
Фантастика:
фэнтези
8.83
рейтинг книги
Мама из другого мира. Делу - время, забавам - час

Решала

Иванов Дмитрий
10. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Решала