← На главную

Конкурентность, интерактивность, мутабельность — выберите два

27.07.2026 06:38 · hackernews

Представьте: у вас запущена программа на Common Lisp, несколько потоков обрабатывают сетевые соединения, другие гоняют данные в фоне. Вы тщательно разрулили все гонки, с конкурентным доступом порядок. Через Emacs и SLIME цепляетесь к серверу, смотрите состояние — интерактивность на месте. Замечаете, что в глобальной хеш-таблице засело неверное значение, и на лету исправляете его простым REMHASH. Мутабельность работает. Но ровно в этот момент другой поток читает ту же таблицу — конец игры.

Три желания — конкурентность, интерактивность и мутабельность — одновременно не исполняются. Конкурентность даёт скорость, но заставляет защищать данные от взаимных блокировок, голодания и порчи памяти. Интерактивность манит удобством: всегда можно залезть в рантайм и подправить состояние. Мутабельность просто практична. Однако совместить их без боли нельзя.

Самый массовый выбор — выкинуть интерактивность. Никто не лазит в данные извне, проблемы параллелизма решаются штатно. Так живут C, Rust, Go. Быстро и безопасно, но отладка сложных систем превращается в мучение, если перезапуск не вариант.

Другой путь — пожертвовать конкурентностью. Потоки остаются, но читать и писать данные одновременно невозможно. Python и Ruby реализуют это через GIL (Global Interpreter Lock): любой доступ к объектам защищён глобальной блокировкой. Вы спокойно работаете в шелле Python или через Pry в Ruby, меняете что угодно и не боитесь гонок. Плата — скорость. В нормальных конкурентных программах синхронизация нужна лишь для крошечной доли обращений, а здесь лок вешается на всё подряд, и тормоза неизбежны.

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

Чудес не бывает: выбор языка — это всегда выбор компромиссов. Производительный софт обычно отказывается от интерактивности, чтобы минимизировать разделяемые данные и синхронизацию. Те, кому нужна интерактивность, мирятся с накладными расходами глобальной блокировки или иммутабельной модели. Разработчики на Common Lisp любят хвастаться, что им доступно всё и сразу, но они вынуждены постоянно помнить: любое действие в REPL способно обрушить программу.

Читать оригинал →