Перейти к основному содержимому

Асинхронность — итоги

Разработчику Аналитику Тестировщику Архитектору Инженеру

Кратко — что стоит унести из раздела "Асинхронность". Если пункт кажется туманным — откройте живые примеры или указанную главу, либо оглавление.


FAQ — Часто задаваемые вопросы

Типичные сбои при работе с процессами, потоками и асинхронным кодом, плюс формулировки, как их ищут в Google — с кратким ответом и ссылкой на главу. Определения для зачёта — в чек-листе.

Вопрос. При нажатии кнопки интерфейс "замирает" на несколько секунд — окно не двигается.

Ответ. Долгая операция выполняется в UI-потоке синхронно. Вынесите сеть и диск в фон (async, worker, отдельный поток) и обновляйте UI только из главного потока. Подробнее здесь — асинхронное выполнение.

Вопрос. Написал async function, а результат — [object Promise], а не данные.

Ответ. Async-функция всегда возвращает Promise; нужен await или .then() в вызывающем коде. Не забывайте await на каждом асинхронном шаге в цепочке. Подробнее здесь — асинхронное выполнение.

Вопрос. Забыл await — программа идёт дальше, потом "иногда" падает.

Ответ. Без await вы получаете незавершённый Promise и гонку: код использует данные до их прихода. Включите линтер на floating promises и трактуйте async-вызовы как обязательный await. Подробнее здесь — асинхронное выполнение.

Вопрос. Вложенные callbacks уехали вправо на экран — код не читается.

Ответ. Это callback hell. Разбейте на именованные функции, используйте Promise/async-await или pipeline-библиотеки. Подробнее здесь — асинхронное выполнение.

Вопрос. Два быстрых клика "Отправить" — на сервер улетело два заказа.

Ответ. Нет идемпотентности и блокировки UI на время запроса. Disable кнопки, debounce, токен запроса на сервере. Подробнее здесь — асинхронное выполнение, IPC.

Вопрос. Счётчик в многопоточном коде "теряет" единицы — 1000 инкрементов дают 987.

Ответ. Классическая гонка данных — read-modify-write без синхронизации. Используйте mutex, lock или атомарный increment. Подробнее здесь — управление потоками.

Вопрос. Программа зависла навсегда — в отладчике оба потока ждут друг друга.

Ответ. Deadlock — захватили два lock в разном порядке. Фиксируйте порядок захвата, используйте try-lock с таймаутом, уменьшайте критические секции. Подробнее здесь — управление потоками.

Вопрос. CPU 100% на одном ядре, прогресса нет — spinlock?

Ответ. Возможны live-lock или busy-wait без полезной работы. Замените spinlock на блокирующий mutex там, где ожидание долгое; проверьте условие выхода из цикла ожидания. Подробнее здесь — управление потоками.

Вопрос. Создал по потоку на каждый запрос — через минуту "too many threads".

Ответ. Потоки дороги; нужен пул потоков или async I/O. Ограничьте параллелизм семафором, переиспользуйте worker pool. Подробнее здесь — процессы и потоки, асинхронное выполнение.

Вопрос. Node.js: один fs.readFileSync в обработчике — весь сервер перестал отвечать.

Ответ. Синхронный I/O блокирует event loop. Используйте async API или worker threads для тяжёлых задач. Подробнее здесь — асинхронное выполнение.

Вопрос. setTimeout(..., 0) и setImmediate — порядок вызовов каждый раз разный.

Ответ. Очереди микрозадач и макрозадач не гарантируют "интуитивный" порядок между типами. Не стройте логику на таймерах без явной цепочки Promise. Подробнее здесь — асинхронное выполнение.

Вопрос. Parallel.ForEach или threads на общий список — элементы пропадают или дублируются.

Ответ. Коллекция не thread-safe для одновременной записи. Используйте concurrent-коллекции, локальные списки с merge или partition без shared mutable state. Подробнее здесь — управление потоками.

Вопрос. WebSocket "молча" отключился — UI не узнал об обрыве.

Ответ. Нужны heartbeat/ping, обработка onclose/onerror и reconnect с backoff. Не полагайтесь только на успешную отправку. Подробнее здесь — IPC, асинхронное выполнение.

Вопрос. UnhandledPromiseRejectionWarning — программа вроде работает, в логе красное.

Ответ. Ошибка в async-цепочке не поймана. Добавьте .catch() или try/catch вокруг await; в Node 15+ такие rejection могут завершить процесс. Подробнее здесь — асинхронное выполнение, ошибки.

Вопрос. Пытаюсь await внутри обычной функции — синтаксическая ошибка.

Ответ. await допустим только в async-функции или top-level module (где язык разрешает). Поднимите async на уровень вызывающей функции. Подробнее здесь — асинхронное выполнение.

Вопрос. Python: asyncio и threading — когда что использовать?

Ответ. Asyncio для массового I/O-bound в одном потоке; threads — для блокирующих библиотек и частичного CPU-bound (с осторожностью из-за GIL). Подробнее здесь — асинхронное выполнение, процессы.

Вопрос. Дочерний процесс не видит переменную родителя — мы же "в одной программе"?

Ответ. Процессы изолированы; память общая только у потоков одного процесса. Обмен — через pipe, socket, shared memory или сообщения. Подробнее здесь — процессы, IPC.

Вопрос. Pipe между процессами "завис" — оба ждут.

Ответ. Буфер pipe конечен; writer блокируется, пока reader не прочитает. Читайте и пишите в разных потоках/процессах асинхронно или увеличьте буфер осознанно. Подробнее здесь — IPC.

Вопрос. REST или gRPC для внутреннего сервиса — как не ошибиться на старте?

Ответ. REST проще отладить в браузере; gRPC — бинарный контракт и стримы для высокой нагрузки между сервисами. Для учебного MVP начните с REST/OpenAPI. Подробнее здесь — IPC, интеграция.

Вопрос. Thread pool queue растёт — запросы висят в очереди минутами.

Ответ. Пул исчерпан долгими задачами. Увеличьте pool осознанно, сократите время задачи, вынесите блокирующий I/O в async. Подробнее здесь — процессы, метрики.

Вопрос. Один поток никогда не получает CPU — остальные живут.

Ответ. Возможно starvation из-за приоритетов или unfair lock. Используйте fair semaphore, снижайте время удержания lock. Подробнее здесь — управление потоками.

Вопрос. SSE или WebSocket для ленты новостей в браузере?

Ответ. SSE — односторонний поток от сервера, проще через HTTP; WebSocket — двусторонний канал для чата и игр. Подробнее здесь — асинхронное выполнение, IPC.

Вопрос. Web Worker не может обновить DOM — "document is undefined".

Ответ. Worker работает без доступа к DOM; шлите сообщения в main thread через postMessage. Подробнее здесь — асинхронное выполнение.

Вопрос. Два пользователя одновременно бронируют последнее место — оба видят "успех".

Ответ. Нужна транзакция или optimistic lock на уровне БД, а не только async UI. Параллельные запросы без координации дают overselling. Подробнее здесь — управление потоками, базы данных.

Вопрос. async void в C# — исключение "проглотилось" и упало приложение.

Ответ. async void только для event handlers; иначе используйте async Task и await, чтобы ошибки всплывали. Подробнее здесь — асинхронное выполнение.

Вопрос. Баг проявляется "раз в десять" — подозреваю потоки, как ловить?

Ответ. Воспроизведите под нагрузкой, включите thread sanitizer / stress test, логируйте порядок захвата lock. Heisenbug часто исчезает в отладчике из-за timing. Подробнее здесь — управление потоками, отладка.

Вопрос. Мобильная ОС "убила" фоновую задачу — пользователь не получил push.

Ответ. Фоновые процессы ограничены политикой OS; используйте push-сервисы, WorkManager/BGTaskScheduler, не полагайтесь на бесконечный thread. Подробнее здесь — процессы.

Вопрос. Что такое асинхронность в программировании?

Ответ. Выполнение задач без блокировки потока на ожидании I/O — пока ждём сеть, делаем другую работу. Подробнее здесь — асинхронное выполнение.

Вопрос. Чем асинхронный код отличается от синхронного?

Ответ. Синхронный ждёт ответ в той же цепочке вызовов; асинхронный отдаёт управление и продолжает по callback/await, когда результат готов. Подробнее здесь — асинхронное выполнение.

Вопрос. Что такое многопоточность (multithreading)?

Ответ. Несколько потоков в одном процессе делят память и выполняются параллельно или по очереди на ядрах CPU. Подробнее здесь — процессы и потоки.

Вопрос. Чем процесс отличается от потока (thread)?

Ответ. Процесс — изолированное приложение со своей памятью; потоки внутри процесса память разделяют. Подробнее здесь — процессы.

Вопрос. Что такое deadlock (взаимная блокировка)?

Ответ. Два потока ждут замки друг друга — прогресс невозможен без внешнего вмешательства. Подробнее здесь — управление потоками.

Вопрос. Что такое race condition (гонка данных)?

Ответ. Результат зависит от порядка потоков — два одновременных write/read без синхронизации. Подробнее здесь — управление потоками.

Вопрос. Что такое mutex (мьютекс)?

Ответ. Примитив — только один поток в критической секции; остальные ждут unlock. Подробнее здесь — управление потоками.

Вопрос. Что такое async await в JavaScript и C#?

Ответ. Синтаксис для ожидания асинхронной операции без блокировки UI-потока — код выглядит линейно. Подробнее здесь — асинхронное выполнение.

Вопрос. Что такое Promise в JavaScript?

Ответ. Объект будущего результата — pending, fulfilled, rejected; цепочки через .then / await. Подробнее здесь — асинхронное выполнение, JavaScript.

Вопрос. Что такое event loop в Node.js?

Ответ. Цикл обработки очереди задач и I/O в одном потоке — callbacks и microtasks после завершения системных операций. Подробнее здесь — асинхронное выполнение.

Вопрос. Что такое callback (функция обратного вызова)?

Ответ. Функция, которую вызывают после завершения операции — классический стиль async до Promise. Подробнее здесь — асинхронное выполнение.

Вопрос. Что такое WebSocket?

Ответ. Протокол двустороннего канала поверх TCP — чат, игры, live-данные без polling. Подробнее здесь — IPC, асинхронное выполнение.

Вопрос. Что такое корутина?

Ответ. Легковесная задача с кооперативной передачей управления — Kotlin, Go goroutine, Python asyncio; дешевле потока OS. Подробнее здесь — асинхронное выполнение.

Вопрос. Чем конкурентность отличается от параллелизма?

Ответ. Конкурентность — логическое чередование задач; параллелизм — одновременно на разных ядрах. Подробнее здесь — процессы, параллельные вычисления.

Вопрос. Что такое IPC (межпроцессное взаимодействие)?

Ответ. Обмен данными между процессами — pipe, socket, shared memory, RPC. Подробнее здесь — IPC.

Вопрос. Что такое thread pool (пул потоков)?

Ответ. Фиксированный набор потоков для очереди задач — не создавать thread на каждый запрос. Подробнее здесь — процессы, управление потоками.

Вопрос. SSE (Server-Sent Events) — что это?

Ответ. Односторонний поток событий от сервера к браузеру по HTTP — проще WebSocket для лент и уведомлений. Подробнее здесь — асинхронное выполнение.

Вопрос. Что такое gRPC?

Ответ. RPC-фреймворк на HTTP/2 и Protobuf — быстрые typed-вызовы между микросервисами. Подробнее здесь — IPC, интеграция.

Вопрос. Blocking vs non-blocking I/O — в чём разница?

Ответ. Blocking — поток стоит до конца read/write; non-blocking возвращает управление сразу, результат позже. Подробнее здесь — асинхронное выполнение.

Вопрос. Что такое asyncio в Python?

Ответ. Модуль async/await и event loop для I/O-bound задач в одном потоке. Подробнее здесь — асинхронное выполнение, Python.

Вопрос. Что такое Web Workers в браузере?

Ответ. Отдельный поток JS без доступа к DOM — тяжёлые вычисления не блокируют страницу. Подробнее здесь — асинхронное выполнение.

Вопрос. Что такое semaphore (семафор) в многопоточности?

Ответ. Счётчик доступа — N потоков одновременно к ресурсу (пул соединений). Подробнее здесь — управление потоками.

Вопрос. Зачем нужна асинхронность в веб-разработке?

Ответ. Сервер и браузер обслуживают много соединений без thread на каждый запрос — отзывчивый UI и масштабируемый API. Подробнее здесь — асинхронное выполнение, frontend.


Что запомнить

Асинхронность — это фундаментальный принцип современного программирования, позволяющий эффективно использовать ресурсы вычислительных систем. Она особенно важна в условиях, когда задачи зависят от внешних операций ввода-вывода, таких как сетевые запросы, работа с диском или взаимодействие с пользователем. Вместо того чтобы блокировать выполнение программы на время ожидания, асинхронные подходы позволяют продолжать обработку других задач, повышая отзывчивость и производительность приложений.

Ключевыми концепциями, лежащими в основе асинхронности, являются процессы и потоки. Процесс представляет собой изолированный экземпляр запущенной программы со своим адресным пространством, что обеспечивает безопасность, но усложняет обмен данными. Потоки, напротив, разделяют память внутри одного процесса, что делает их легковесными и удобными для координации, но требует тщательного управления доступом к общим данным.

Для предотвращения гонок данных и обеспечения корректной работы многопоточных программ используются механизмы синхронизации — мьютексы, семафоры, спинлоки и атомарные операции. Каждый из них решает определённый класс задач — от защиты критических секций до ограничения параллельного доступа к ресурсам с ограниченной пропускной способностью. Однако неправильное использование этих механизмов может привести к таким проблемам, как взаимная блокировка (deadlock), голодание (starvation) или активное ожидание без прогресса (live-lock).

Современные языки программирования предоставляют высокоуровневые абстракции для работы с асинхронностью — корутины, async/await, обещания (promises), которые скрывают сложность низкоуровневого управления потоками и упрощают написание читаемого и поддерживаемого кода. На уровне системного взаимодействия асинхронность реализуется через очереди сообщений, события и специализированные протоколы, такие как WebSocket, SSE или gRPC, что позволяет строить масштабируемые и отзывчивые распределённые системы.

Понимание различий между конкурентностью и параллелизмом, а также умение выбирать правильные инструменты для конкретной задачи — важнейший навык для разработчика, стремящегося создавать эффективные и надёжные приложения.


Куда идти дальше

ТемаРаздел
"Проект, структура и фреймворки — о разделе""Проект, структура и фреймворки — о разделе"
"Архитектура выполнения — о разделе""Архитектура выполнения — о разделе"
"Выполнение кода — о разделе""Выполнение кода — о разделе"
"Парадигмы и уровни абстракции — о разделе""Парадигмы и уровни абстракции — о разделе"

Проверьте себя: Чек-лист самопроверки.