Эти два понятия очень важны, и вы должны постараться их хорошенько осмыслить. Процессом (process) называется экземпляр вашей программы, загруженной в память. Этот экземпляр может создавать потоки (thread), которые представляют собой последовательность инструкций на выполнение. Важно понимать, что выполняются не процессы, а именно потоки. Причем любой процесс имеет хотя бы один поток. Этот поток называется главным (основным) потоком приложения.
Так как практически всегда потоков гораздо больше, чем физических процессоров для их выполнения, то потоки на самом деле выполняются не одновременно, а по очереди. (Заметьте, что распределение процессорного времени происходит именно между потоками, так как, еще раз повторю, процессы не выполняются.) Но переключение между ними происходит так часто, что кажется будто они выполняются параллельно.
В зависимости от ситуации потоки могут находиться в трех состояниях. Давайте посмотрим, что это за состояния. Во-первых, поток может выполняться, когда ему выделено процессорное время, т.е. он может находиться в состоянии активности. Во-вторых, он может быть неактивным и ожидать выделения процессора, т.е. быть в состоянии готовности. И есть еще третье, тоже очень важное состояние – состояние блокировки. Когда поток заблокирован, ему вообще не выделяется время. Обычно блокировка ставится на время ожидания какого-либо события. При возникновении этого события поток автоматически переводится из состояния блокировки в состояние готовности. Например, если один поток выполняет вычисления, а другой должен ждать результатов, чтобы сохранить их на диск. Второй мог бы использовать цикл типа "while (!isCalcFinished) continue;", но легко убедиться на практике, что во время выполнения этого цикла процессор занят на 100% (это называется активным ожиданием). Таких вот циклов следует по возможности избегать, в чем нам оказывает неоценимую помощь механизм блокировки. Второй поток может заблокировать себя до тех пор, пока первый не установит событие, сигнализирующее о том, что чтение окончено.
В системе выделяются два вида потоков – интерактивные, крутящие свой цикл обработки сообщений (такие, как главный поток приложения), и рабочие, представляющие собой простую функцию. Во втором случае поток завершается по мере завершения выполнения этой функции.
Заслуживающим внимания моментом является также способ организации очередности потоков. Можно было бы, конечно, обрабатывать все потоки по очереди, но такой способ далеко не самый эффективный. Гораздо разумнее оказалось ранжировать все потоки по приоритетам. Приоритет потока обозначается числом от 0 до 31, и определяется исходя из приоритета процесса, породившего поток, и относительного приоритета самого потока. Таким образом, достигается наибольшая гибкость, и каждый поток в идеале получает столько времени, сколько ему необходимо.
Иногда приоритет потока может изменяться динамически. Так интерактивные потоки, имеющие обычно класс приоритета Normal, система обрабатывает несколько иначе и несколько повышает фактический приоритет таких потоков, когда процесс, их породивший, находится на переднем плане (foreground). Это сделано для того, чтобы приложение, с которым в данный момент работает пользователь, быстрее реагировало на его действия.
Конечно, эта заметка не претендует на исчерпывающее описание использования многозадачности. Цель этой вводной статьи – заинтересовать темой. В дальнейшем мы с вами поговорим о таких вещах, как создание интерфейсных потоков и синхронизация между потоками. А если вы хотите посмотреть пример создания рабочего потока, то он рассматривался в рассылке №5 от 28 июня.
ВОПРОС-ОТВЕТ