Чтение онлайн

ЖАНРЫ

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание

Троелсен Эндрю

Шрифт:

Замечание. Если столбец PID в окне Диспетчер задач Windows не отображается, выберите в этом окне команду Вид->Выбрать столбцы… из меню и в открывшемся после этого окне установите флажок Идентификатор процесса (PID).

Рис. 13.1. Диспетчер задач Windows

Обзор потоков

Каждый процесс Win32 имеет один главный "поток", выполняющий функции точки входа в приложение. В следующей главе будет выяснено, как создавать дополнительные потоки и соответствующий программный код, применяя возможности пространства имен System.Threading, но пока что для освещения вопросов, представленных здесь, нам нужно выполнить определенную вспомогательную работу. Во-первых, заметим, что поток – это "нить" выполнения в рамках данного процесса. Первый поток, созданный точкой входа процесса, называется первичным потоком. Приложения Win32 с графическим интерфейсом пользователя определяют в качестве точки входа приложения метод WinMain. Консольные приложения для этой цели используют метод Main. Процессы, состоящие из одного первичного потока, будут потокоустойчивыми, поскольку

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

Учитывая эти потенциальные недостатки однопоточных приложений, Win32 API позволяет первичным потокам порождать дополнительные вторичные потоки (также называемые рабочими потоками), используя, например, такую удобную функцию Win32 API, как CreateThread. Каждый поток (первичный или вторичный) в таком процессе становится уникальным элементом выполнения и получает доступ ко всем открытым элементам данных на условиях конкуренции.

Вы можете сами догадаться, что разработчики создают дополнительные потоки, как правило, для того, чтобы программа могла быстрее реагировать на. действия пользователя. Многопоточные процессы обеспечивают иллюзию того, что все действия выполняются приблизительно за одно и то же время. Например, приложение может порождать рабочий поток для выполнения действий, требующих много времени (снова вспомним о печати большого файла). Как только созданный вторичный поток начинает свою работу, главный поток снова получает возможность отвечать на пользовательский ввод, что позволяет процессу в целом потенциально обеспечить лучшую производительность. Хотя в реальности этого может и не произойти: использование слишком большого числа потоков в рамках одного процесса может ухудшить производительность, поскольку процессору придется часто переключаться между активными потоками (а это требует немало времени).

В реальности всегда следует учитывать то, что многопоточность является, по сути, иллюзией, обеспечиваемой операционной системой. Машины, основанные на одним процессоре, в действительности не способны обрабатывать несколько потоков одновременно. Вместо этого системы с одним процессором выделяют каждому потоку свою) часть времени (что называют квантованием времени) на основе уровня приоритета данного потока. Когда квант времени потока заканчивается, текущий поток приостанавливается, чтобы свою задачу мог выполнить другой поток. Чтобы поток "помнил", что происходило перед тем, как поток был временно "отодвинут в сторону", каждый поток получает возможность записать необходимые данные в блик TLS (Thread Local Storage – локальная память потока), и каждому потоку обеспечивается отдельный стек вызовов, как показано на рис. 13.2.

Если тема потоков для вас нова, не слишком беспокойтесь о деталях. На этот момент достаточно запомнить только то, что поток является уникальной "нитью" выполнения в рамках процесса Win32. Каждый процесс имеет первичный поток (создаваемый точкой входа в приложение) и может содержать дополнительные потоки, которые создаются программными средствами.

Замечание. Новые процессоры Intel имеют особенность, называемую технологией НТ (Hyper-Threading Technology – гиперпотоковая технология), которая позволяет одному процессору при определенных условиях обрабатывать множество потоков одновременно. Подробности описания этой технологии можно найти по адресу http://www.intel.com/info/hyperthreading.

Рис. 13.2. Взаимосвязь процесса и потоков Win32

Взаимодействие с процессами в рамках платформы .NET

Хотя процессы и потоки сами по себе не являются чем-то новым, способы взаимодействия с этими примитивами в рамках платформы .NET существенно изменены (к лучшему). Чтобы успешно пройти путь к пониманию приемов построения компоновочных блоков с поддержкой множества потоков (см. главу 14), мы начнем с обсуждения возможностей взаимодействия с процессами на основе использования библиотек базовых классов .NET.

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

Таблица 13.1. Избранные члены пространства имен System.Diagnostics

Типы System.Diagnostics для поддержки процессов Описание
Process Класс Process обеспечивает доступ к локальным и удаленным процессам, а также позволяет программно запускать и останавливать процессы
ProcessModule Этот тип представляет модуль (*.dll или *.exe), загруженный в рамках конкретного процесса. При этом тип ProcessModule может представлять любой модуль – модуль COM, модуль .NET или традиционный двоичный файл C
ProcessModuleCollection Предлагает строго типизованную коллекцию объектов ProcessModule
ProcessStartlnfo Указывает множество значений, используемых при запуске процесса с помощью метода Process.Start
ProcessThread Представляет поток в рамках данного процесса. Тип ProcessThread используется для диагностики множества потоков процесса, а не для того, чтобы порождать новые потоки выполнения в рамках данного процесса
ProcessThreadCollection Предлагает строго типизованную коллекцию объектов PrосessThread 

Тип System.Diagnostics.Process

позволяет проанализировать процессы, выполняемые на данной машине (локальной или удаленной). Класс Process предлагает также члены, которые позволяют запускать и останавливать процессы программными средствами, устанавливать уровни приоритета и получать список активных потоков и/или загруженных модулей, выполняемых в рамках данного процесса. В табл. 13.2 предлагается список некоторых (но не всех) членов System.Diagnostics.Process.

Таблица 13.2. Избранные члены типа Process

Член Описание
ExitCode Свойство, содержащее значение, которое указывается процессом при завершении его работы. Для получения этого значения необходимо обработать событие Exited (при асинхронном уведомлении) или вызвать метод WaitForExit (при синхронном уведомлении)
ExitTime Свойство, содержащее штамп времени, соответствующий прекращению работы процесса (и представленный типом DateTime)
Handle Свойство, возвращающее дескриптор, назначенный процессу операционной системой
HandleCount Свойство, возвращающее число дескрипторов, открытых процессом
Id Свойство, содержащее идентификатор процесса (PID) для данного процесса
MachineName Свойство, содержащее имя компьютера, на котором выполняется данный процесс
MainModule Свойство, получающее тип ProcessModule, который представляет главный модуль данного процесса
MainWindowTitle MainWindowHandle Свойство MainWindowTitle получает заголовок главного окна процесса (если процесс не имеет главного окна, будет возвращена пустая строка). Свойство MainWindowHandle получает дескриптор (представленный типом System.IntPtr) соответствующего окна. Если процесс не имеет главного окна, типу IntPtr присваивается значение System.IntPtr.Zero
Modules Свойство, обеспечивающее доступ к строго типизованной коллекции ProcessModuleCollection, представляющей множество модулей (*.dll или *.exe), загруженных в рамках текущего процесса
PriorityBoostEnabled Это свойство указывает, должна ли операционная система временно ускорять выполнение процесса, когда его главное окно получает фокус ввода
PriorityClass Свойство, позволяющее прочитать или изменить данные базового приоритета соответствующего процесса
ProcessName Свойство, содержащее имя процесса (которое, как вы можете догадаться, соответствует имени приложения)
Responding Значение этого свойства указывает, должен ли пользовательский интерфейс процесса реагировать на действия пользователя
StartTime Свойство с информацией о времени, соответствующем старту данного процесса (эта информация представлена типом DateTime)
Threads Свойство, получающее набор потоков, выполняющихся в рамках данного процесса (представляется массивом типов ProcessThread)
CloseMainWindow Метод, завершающий процесс с пользовательским интерфейсом путем отправки соответствующего сообщения о закрытии главного окна
GetCurrentProcess Статический метод, возвращающий тип Process, используемый для представления процесса, активного в настоящий момент
GetProcesses Статический метод, возвращающий массив компонентов Process, выполняющихся на данной машине
Kill Метод, немедленно прекращающий выполнение соответствующего процесса
Start Метод, начинающий выполнение процесса

Список выполняемых процессов

Чтобы привести пример обработки типов Process, предположим, что у нас есть консольное приложение C# ProcessManipulator, которое определяет следующий вспомогательный статический метод.

public static void ListAllRunningProcesses {

 // Получение списка процессов, выполняемых на данной машине.

 Process[] runningProcs = Process.GetProcesses(".");

 // Печать значения PID и имени каждого процесса.

 foreach(Process p in runningProcs) {

string info = string.Format("-› PID: {0}\tИмя: {1}", p.Id, p.ProcessName);

Console.WriteLine(info);

 }

 Console.WriteLine("*************************************\n");

}

Обратите внимание на то, что статический метод Process.GetProcesses возвращает массив типов Process, представляющих процессы, запущенные на выполнение на целевой машине (используемая здесь точка обозначает локальный компьютер).

После получения массива типов Process можно использовать любой из членов, приведенных в табл. 13.2. Здесь просто отображается значение PID и имя каждого из процессов. В предположении о том, что вы обновили метод Main для вызова ListAllRunningProcesses, в результате выполнения соответствующей программы вы должны увидеть нечто подобное показанному на рис. 13.3.

Рис. 13.3. Перечень запущенных процессов

Чтение данных конкретного процесса

Поделиться с друзьями: