Что делает код

Что делает код

@python_quiz

Разберем этот квиз

Код из квиза:

python

Кратко: главный поток запускает три потока и сразу печатает длину списка x, не ожидая завершения запущенных потоков.

Почему результат неопределён

  • Запуск потоков асинхронен: метод start() лишь ставит поток в планировщик, он может начать выполняться немедленно, а может быть запущен позже.
  • В главном потоке нет ожидания (join) — поэтому print(len(x)) выполняется сразу после старта потоков.
  • Добавления в список (x.append(1)) в CPython выполняются «атомарно» на уровне интерпретатора благодаря GIL — это предотвращает повреждение структуры списка, но не гарантирует, что все три append выполнятся до вызова print.
  • Следовательно, в момент вызова print количество элементов в x может быть любым от 0 до 3 включительно — в зависимости от планирования потоков и скорости их запуска/выполнения.

Разбор вариантов (пояснения)

  • "Всегда напечатает 0" — неверно. Потоки вполне могут выполниться до print, и тогда в x появятся элементы.
  • "Всегда напечатает 3" — неверно. Нет механизма ожидания; print может выполниться до того, как хоть один поток успеет выполнить append.
  • "Может напечатать 0, 1, 2 или 3" — это корректное описание поведения: возможны все перечисленные варианты.
  • "Код упадёт с ошибкой" — неверно. Операции start и append корректны, ошибок не ожидается.

Демонстрация: статистика при многократных запусках

Простой эксперимент, который выполняет много итераций и считает, какие результаты встречаются:

python

Вывод зависит от машины и загрузки ОС. На одной машине чаще встретится 2 или 3, на другой — больше нулей/единиц. Главное — результаты непредсказуемы и зависят от планировщика.

Как гарантировать determinism (всегда получить 3)

Нужно дождаться завершения потоков. Например, сохранить объекты потоков и вызвать join:

python

Альтернативы: использовать ThreadPoolExecutor и wait, или синхронизировать выполнение через Event/Barrier.

Дополнительные замечания

  • Доступ к общим структурам данных в многопоточном коде обычно требует внимательности: даже если append атомарен в CPython, более сложные операции (читать, изменять, записывать) могут потребовать блокировок (Lock) для корректной логики.
  • Поведение, описанное выше — свойство планирования потоков, а не ошибка языка. Всегда явным образом синхронизируйте потоки, если результат зависит от их завершения.

Вывод: без синхронизации длина списка в момент print может быть любой от 0 до 3.

Report Page