Что делает код
@python_quizРазберем этот квиз
Код из квиза:

Кратко: главный поток запускает три потока и сразу печатает длину списка 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 корректны, ошибок не ожидается.
Демонстрация: статистика при многократных запусках
Простой эксперимент, который выполняет много итераций и считает, какие результаты встречаются:

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

Альтернативы: использовать ThreadPoolExecutor и wait, или синхронизировать выполнение через Event/Barrier.
Дополнительные замечания
- Доступ к общим структурам данных в многопоточном коде обычно требует внимательности: даже если append атомарен в CPython, более сложные операции (читать, изменять, записывать) могут потребовать блокировок (Lock) для корректной логики.
- Поведение, описанное выше — свойство планирования потоков, а не ошибка языка. Всегда явным образом синхронизируйте потоки, если результат зависит от их завершения.
Вывод: без синхронизации длина списка в момент print может быть любой от 0 до 3.