В прошлом уроке мы выяснили, зачем нужна многопоточность. Теперь давайте разберёмся в терминах — потому что слова «процесс», «поток», «асинхронность» и «многопоточность» часто путают, а разница между ними принципиальна. Если не разобраться сейчас — потом будет каша в голове, которая аукнется на практике. Начнём с самого верхнего уровня. Когда вы запускаете программу — любую: блокнот, браузер, свою консольную утилиту — операционная система создаёт для неё процесс.
Откройте диспетчер задач — там десятки процессов. Браузер, мессенджер, антивирус, ваша IDE. Каждый живёт в своём «пузыре». И это хорошо: если блокнот упадёт с ошибкой — ваша игра продолжит работать. Процессы изолированы друг от друга. Было бы странно, если бы крах калькулятора завершал Word, правда?
Внутри каждого процесса работает хотя бы один поток.
Вот тут ключевой момент: потоки внутри одного процесса делят память. Это и благо (можно обмениваться данными), и проклятие (можно испортить данные друг друга). Именно из-за общей памяти возникают все проблемы многопоточности, о которых мы будем говорить в следующих уроках.
Все программы, которые мы писали до этого момента, работали в одном потоке — основном. Когда вы пишете Console.ReadLine() и программа ждёт ввода — весь единственный поток стоит. Когда цикл foreach перебирает миллион элементов — весь поток занят этим перебором. Ничего другого он делать не может.
В C# есть два принципиально разных подхода к многозадачности. Они решают разные проблемы разными способами, и путать их — одна из самых частых ошибок новичков.
Многопоточность — это когда вы создаёте дополнительных работников.
Представьте: вам нужно за два часа пройти урок по многопоточности и прибраться в квартире. Если вы один — придётся делать всё последовательно: сначала уборка, потом урок (или наоборот). В каждый момент времени вы заняты чем-то одним.
Но дома оказался ваш друг. Вы говорите: «Слушай, приберись, пока я учусь». Теперь два дела выполняются параллельно двумя разными исполнителями. Вы — основной поток, друг — второй поток. Каждый делает своё. Когда друг закончит, он скажет: «Готово!» — и вы узнаете, что уборка завершена.
Именно так работает многопоточность в программировании: вы создаёте дополнительный поток и поручаете ему задачу. Оба потока работают одновременно, каждый на своём ядре процессора.
Асинхронность** — это когда вы не стоите без дела, пока ждёте.
Другая ситуация. Вы работаете дома и решили выпить чаю. Идёте на кухню, ставите чайник и... стоите рядом. Ждёте. Не проверяете почту, не читаете — просто стоите и смотрите на чайник. Только когда он закипит — наливаете чай и возвращаетесь к работе. Это однопоточное выполнение: пока ждёте чайник — всё остальное стоит.
А теперь другой вариант. Включаете чайник и сразу возвращаетесь за стол. Пока вода греется — пишете код, отвечаете на сообщения, читаете документацию. Через пять минут слышите щелчок — чайник закипел. Прерываетесь на минуту, завариваете чай, и продолжаете работу.
Заметьте: вам не понадобился второй человек. Вы справились сами. Просто не стояли без дела, пока ждали. Вот это и есть асинхронность.
Ключевое отличие: при многопоточности вы нанимаете второго работника. При асинхронности — один работник просто не простаивает. Это принципиально разные механизмы, хотя цель у обоих одна — не тратить время впустую. Давайте сведём это в таблицу — потому что путаница между этими подходами стоила разработчикам тысяч часов отладки:
| Многопоточность | Асинхронность | |
|---|---|---|
| Суть | Несколько потоков работают параллельно | Один поток не простаивает в ожидании |
| Аналогия | Позвать друга помочь с уборкой | Не стоять у чайника |
| Для каких задач | CPU-bound: вычисления, обработка данных | IO-bound: сеть, файлы, базы данных |
| Инструменты C# | Thread, Task.Run, Parallel |
async / await, Task |
| Потоков | Несколько | Поток освобождается на время ожидания (продолжение может выполниться в другом потоке) |
| Сложность | Высокая (синхронизация, гонки, deadlock) | Средняя (нужно понять async/await) |
Подведём итог. В мире C# два инструмента многозадачности, и каждый — для своего случая. Задача нагружает процессор? Многопоточность — раздайте работу нескольким ядрам. Задача ждёт внешний ресурс? Асинхронность — не блокируйте поток, пока ждёте.
В следующих уроках мы начнём с многопоточности — научимся создавать потоки вручную через класс Thread. Асинхронность будет ближе к концу блока — когда у нас будет фундамент для понимания того, как потоки работают под капотом.
Процесс — независимая программа в ОС. Процессы изолированы — ошибка одного не влияет на другие.
Поток (Thread) — минимальная единица выполнения внутри процесса. Потоки одного процесса делят общую память.
Многопоточность — несколько потоков работают параллельно. Для CPU-bound задач: вычисления, обработка данных. Инструменты: Thread, Task.Run, Parallel.
Асинхронность — поток не простаивает в ожидании, а освобождается для другой работы. Для IO-bound задач: сеть, файлы, БД. Инструменты: async/await.
Многопоточность — «нанять второго работника». Асинхронность — «не стоять у чайника».