Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
Если тема потоков для вас нова, то не стоит беспокоиться о деталях. На данном этапе просто запомните, что любой поток представляет собой уникальный путь выполнения внутри процесса Windows. Каждый процесс имеет главный поток (созданный посредством точки входа исполняемого файла) и может содержать дополнительные потоки, которые создаются программно.
Взаимодействие с процессами используя платформу .NET Core
Несмотря на то что с процессами и потоками не связано ничего нового, способ взаимодействия с ними в рамках платформы .NET Core значительно изменился (в лучшую сторону). Чтобы подготовить почву для понимания области построения многопоточных сборок (см. главу 15), давайте начнем с выяснения способов взаимодействия с процессами, используя библиотеки базовых классов .NET Core.
В пространстве имен System.Diagnostics определено несколько типов, которые позволяют программно взаимодействовать с процессами и разнообразными типами, связанными с диагностикой, такими как журнал событий системы и счетчики производительности. В текущей главе нас интересуют только типы, связанные с процессами, которые описаны в табл. 14.1.
Класс System.Diagnostics.Process позволяет анализировать процессы, выполняющиеся на заданной машине (локальные или удаленные). В классе Process также определены члены, предназначенные для программного запуска и завершения процессов, просмотра (или модификации) уровня приоритета процесса и получения списка активных потоков и/или загруженных модулей внутри указанного процесса. В табл. 14.2 перечислены некоторые основные свойства класса System.Diagnostics.Process.
Кроме перечисленных выше свойств в классе System.Diagnostics.Process определено несколько полезных методов (табл. 14.3).
Перечисление выполняющихся процессов
Для иллюстрации способа манипулирования объектами Process создайте новый проект консольного приложения C# по имени ProcessManipulator и определите в классе Program следующий вспомогательный статический метод (не забудьте импортировать в файл кода пространства имен System.Diagnostics и System.Linq):
static void ListAllRunningProcesses()
{
// Получить все процессы на локальной машине, упорядоченные по PID.
var runningProcs =
from proc
in Process.GetProcesses(".")
orderby proc.Id
select proc;
// Вывести для каждого процесса идентификатор PID и имя.
foreach(var p in runningProcs)
{
string info = $"-> PID: {p.Id}tName: {p.ProcessName}";
Console.WriteLine(info);
}
Console.WriteLine("************************************n");
}
Статический метод Process.GetProcesses() возвращает массив объектов Process, которые представляют выполняющиеся процессы на целевой машине (передаваемая методу строка "." обозначает локальный компьютер). После получения массива объектов Process можно обращаться к любым членам, описанным в табл. 14.2 и 14.3. Здесь просто для каждого процесса выводятся идентификатор PID и имя с упорядочением по PID. Модифицируйте операторы верхнего уровня, как показано ниже:
using System;
using System.Diagnostics;
using System.Linq;
Console.WriteLine("***** Fun with Processes *****n");
ListAllRunningProcesses();
Console.ReadLine();
Запустив приложение, вы увидите список имен и идентификаторов PID для всех процессов на локальной машине. Ниже показана часть вывода (ваш вывод наверняка будет отличаться):
***** Fun with Processes *****
-> PID: 0 Name: Idle
-> PID: 4 Name: System
-> PID: 104 Name: Secure System
-> PID: 176 Name: Registry
-> PID: 908 Name: svchost
-> PID: 920 Name: smss
-> PID: 1016 Name: csrss
-> PID: 1020 Name: NVDisplay.Container
-> PID: 1104 Name: wininit
-> PID: 1112 Name: csrss
************************************
Исследование конкретного процесса
В дополнение к полному списку всех выполняющихся процессов на заданной машине статический метод Process.GetProcessById() позволяет получать одиночный объект Process по ассоциированному с ним идентификатору PID. В случае запроса несуществующего PID генерируется исключение ArgumentException. Например, чтобы получить объект Process, который представляет процесс с PID, равным 30592, можно написать следующий код:
// Если процесс с PID, равным 30592, не существует,
// то сгенерируется исключение во время выполнения.
static void GetSpecificProcess()
{
Process theProc = null;
try
{
theProc = Process.GetProcessById(30592);
}
catch(ArgumentException ex)
{
Console.WriteLine(ex.Message);
}
}
К настоящему моменту вы уже знаете, как получить список всех процессов, а также специфический процесс на машине посредством поиска по PID. Наряду с выяснением идентификаторов PID и имен процессов класс Process позволяет просматривать набор текущих потоков и библиотек, применяемых внутри заданного процесса. Давайте посмотрим, как это делается.
Исследование набора потоков процесса
Набор потоков представлен в виде строго типизованной коллекции ProcessThreadCollection, которая содержит определенное количество отдельных объектов ProcessThread. В целях иллюстрации добавьте к текущему приложению приведенный далее вспомогательный статический метод:
static void EnumThreadsForPid(int pID)
{
Process theProc = null;
try
{
theProc = Process.GetProcessById(pID);
}
catch(ArgumentException ex)
{
Console.WriteLine(ex.Message);
return;
}
// Вывести статистические сведения по каждому потоку
// в указанном процессе.
Console.WriteLine(
"Here are the threads used by: {0}", theProc.ProcessName);
ProcessThreadCollection theThreads = theProc.Threads;
foreach(ProcessThread pt in theThreads)
{
string info =
$"-> Thread ID: {pt.Id}tStart Time:
{pt.StartTime.ToShortTimeString()}tPriority:
{pt.PriorityLevel}";
Console.WriteLine(info);
}
Console.WriteLine("************************************n");
}
Как видите, свойство Threads в типе System.Diagnostics.Process обеспечивает доступ к классу ProcessThreadCollection. Здесь для каждого потока внутри указанного клиентом процесса выводится назначенный идентификатор потока, время запуска и уровень приоритета. Обновите операторы верхнего уровня в своей программе, чтобы запрашивать у пользователя идентификатор PID процесса, подлежащего исследованию:
...
// Запросить у пользователя PID и вывести набор активных потоков.
Console.WriteLine("***** Enter PID of process to investigate *****");
Console.Write("PID: ");
string pID = Console.ReadLine();
int theProcID = int.Parse(pID);
EnumThreadsForPid(theProcID);
Console.ReadLine();
После запуска приложения можно вводить PID любого процесса на машине и просматривать имеющиеся внутри него потоки. В следующем выводе