Книга: Программирование на языке Ruby
13.2. Синхронизация потоков
Почему необходима синхронизация? Потому что из-за «чередования» операций доступ к переменным и другим сущностям может осуществляться в порядке, который не удается установить путем чтения исходного текста отдельных потоков. Два и более потоков, обращающихся к одной и той же переменной, могут взаимодействовать между собой непредвиденными способами, и отлаживать такую программу очень трудно.
Рассмотрим простой пример:
x = 0
t1 = Thread.new do
1.upto(1000) do
x = x + 1
end
end
t2 = Thread.new do
1.upto(1000) do
x = x + 1
end
end
t1.join
t2.join
puts x
Сначала переменная x
равна 0. Каждый поток увеличивает ее значение на тысячу раз. Логика подсказывает, что в конце должно быть напечатано 2000.
Но фактический результат противоречит логике. На конкретной машине было напечатано значение 1044. В чем дело?
Мы предполагали, что инкремент целого числа — атомарная (неделимая) операция. Но это не так. Рассмотрим последовательность выполнения приведенной выше программы. Поместим поток t1
слева, а поток t2
справа. Каждый квант времени занимает одну строчку и предполагается, что к моменту, когда был сделан этот мгновенный снимок, переменная x
имела значение 123.
t1 t2
-------------------------- -----------------------------
Прочитать значение x (123)
Прочитать значение x (123)
Увеличить значение на 1 (124)
Увеличить значение на 1 (124)
Записать результат в x
Записать результат в x
Ясно, что каждый поток увеличивает на 1 то значение, которое видит. Но не менее ясно и то, что после увеличения на 1 обоими потоками x
оказалось равно всего 124.
И это лишь самая простая из проблем, возникающих в связи с синхронизацией. Для решения более сложных приходится прилагать серьезные усилия — это предмет изучения специалистами в области теоретической информатики и математики.
- 13.2.1. Синхронизация с помощью критических секций
- 13.2.2. Синхронизация доступа к ресурсам (mutex.rb)
- 13.2.3. Предопределенные классы синхронизированных очередей
- 13.2.4. Условные переменные
- 13.2.5. Другие способы синхронизации
- 13.2.6. Тайм-аут при выполнении операций
- 13.2.7. Ожидание события
- 13.2.8. Продолжение обработки во время ввода/вывода
- 13.2.9. Реализация параллельных итераторов
- 13.2.10. Параллельное рекурсивное удаление
- Достоинства и недостатки потоков
- ЧАСТЬ 3 СИНХРОНИЗАЦИЯ
- 1 Синхронизация данных
- ГЛАВА 8 Синхронизация потоков
- Синхронизация доступа к куче
- 4.4. Синхронизация потоков и критические секции
- А.5. Синхронизация потоков: программы
- Классы синхронизации, внедренные в версии .NET Framework 4.0
- ПРИЛОЖЕНИЕ Б Сопоставление функций Windows, UNIX и библиотеки С
- Функции взаимоблокировки
- Мьютексы
- Сравнительный обзор: мьютексы и объекты CRITICAL_SECTION