Асинхронность — итоги
Кратко — что стоит унести из раздела "Асинхронность". Если пункт кажется туманным — откройте живые примеры или указанную главу, либо оглавление.
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, что позволяет строить масштабируемые и отзывчивые распределённые системы.
Понимание различий между конкурентностью и параллелизмом, а также умение выбирать правильные инструменты для конкретной задачи — важнейший навык для разработчика, стремящегося создавать эффективные и надёжные приложения.
Куда идти дальше
| Тема | Раздел |
|---|---|
| "Проект, структура и фреймворки — о разделе" | "Проект, структура и фреймворки — о разделе" |
| "Архитектура выполнения — о разделе" | "Архитектура выполнения — о разделе" |
| "Выполнение кода — о разделе" | "Выполнение кода — о разделе" |
| "Парадигмы и уровни абстракции — о разделе" | "Парадигмы и уровни абстракции — о разделе" |
Проверьте себя: Чек-лист самопроверки.