#python #многопоточность
Версия Python 3.5, использую модуль threading. Нужна ли синхронизация, когда потоки изменяют общую глобальную переменную в один момент времени? В книгах пишут, что нужно использовать Lock(), но и без него не возникает не каких проблем. Пример кода из книги: import threading, time count = 0 def adder(): global count count += 1 time.sleep(0.5) count += 1 threads = [] for i in range(100): thread = threading.Thread(target=adder, args=()) thread.start() threads.append(thread) for thread in threads: thread.join() print(count) Вот здесь, судя по книге, должно каждый раз выдавать разные числа, так как одновременно 100 потоков в один момент времени меняют переменную count. Но почему-то всё нормально срабатывает. Это в какой-то версии было исправлено или как? Объясните, кто знает.
Ответы
Ответ 1
Атомарные операции. Переключение нитей происходит только между отдельными байт-код операциями. Сами же операции неделимы. Посмотреть, как выглядит байт-код можно с помощью модуля dis Вот некоторые потокобезопасные операции: чтение или изменение одного атрибута объекта чтение или изменение одной глобальной переменной выборка элемента из списка модификация списка "на месте" (т.е. с помощью метода append) выборка элемента из словаря модификация словаря "на месте" (т.е. добавление элемента, или вызов метода clear)Ответ 2
нужна ли синхронизация когда потоки изменяют общую глобальную переменную в один момент времени? Нужна. Вот ещё пример (запускаем 100 потоков, увеличиваем глобальную переменную niters раз): #!/usr/bin/env python3 from concurrent.futures import ThreadPoolExecutor as Pool niters = 100000 count = 0 def increment(): global count count += 1 with Pool(100) as pool: for _ in range(niters): pool.submit(increment) assert count == niters, count Этот код может приводить к AssertionError: Traceback (most recent call last): File "/usr/lib/python3.5/runpy.py", line 184, in _run_module_as_main "__main__", mod_spec) File "/usr/lib/python3.5/runpy.py", line 85, in _run_code exec(code, run_globals) File "./__main__.py", line 17, inassert count == niters, count AssertionError: 99981 Код легко сделать threadsafe, добавлением lock: #!/usr/bin/env python3 import threading from concurrent.futures import ThreadPoolExecutor as Pool niters = 100000 count = 0 def increment(lock=threading.Lock()): global count with lock: count += 1 with Pool(100) as pool: for _ in range(niters): pool.submit(increment) assert count == niters, count Только одна += операция выполняется в любое время (что делает потоки бессмысленными в этом примере. См. видео Thinking about Concurrency, Raymond Hettinger, Python core developer), поэтому гарантируется, что count == niters в конце.
Комментариев нет:
Отправить комментарий