Потоки
А кто в домене живет?
Прилагаемый пример демонстрирует методы анализа процесса, домена и потоков (в .NET потоки и сборки также представлены объектами соответствующих классов).
Здесь показаны потоки в процессе, а также сборки, которые выполняются в домене приложения:
using System;
using System.Windows.Forms;
// Это пространство имен требуется для работы с классом Assembly.
using System.Reflection;
// Это пространство имен требуется для работы с классом Process.
using System.Diagnostics;
namespace AppDomain1
{
class MyAppDomain
{
public static void ShowThreads()
{
Process proc = Process.GetCurrentProcess();
foreach(ProcessThread aPhysicalThread in proc.Threads)
{
Console.WriteLine
(aPhysicalThread.Id.ToString() + ":" + aPhysicalThread.ThreadState);
}
}
public static void ShowAssemblies()
{
// Получили ссылку на домен.
AppDomain ad = AppDomain.CurrentDomain;
// В рамках домена может быть множество сборок.
// Можно получить список сборок домена.
Assembly[] loadedAssemblies = ad.GetAssemblies();
// У домена имеется FriendlyName, которое ему присваивается
// при создании. При этом у него даже нет доступного конструктора.
Console.WriteLine("Assemblies in {0} domain:", ad.FriendlyName);
foreach(Assembly assembly in loadedAssemblies)
{
Console.WriteLine(assembly.FullName);
}
}
static void Main(string[] args)
{
Console.WriteLine("=========================");
// MessageBox.Show("XXX"); – Это всего лишь вызов метода класса
// MessageBox. Вызов выполняется лишь потому, что в домен с самого
// начала загружается сборка System.Windows.Forms.dll.
MessageBox.Show("XXX");
ShowThreads();
ShowAssemblies();
Console.WriteLine("=========================");
// Даже в таком простом приложении в рамках домена приложения
// живут три сборки!
}
}
}
Листинг
15.1.
Обзор пространства имен System.Threading
В этом пространстве объявляются типы, которые используются для создания многопоточных приложений: работа с потоком, средства синхронизации доступа к общим данным, примитивный вариант класса Timer... Много всего!
Класс Thread. Общая характеристика
Thread -класс представляет управляемые потоки: создает потоки и управляет ими — устанавливает приоритет и статус потоков. Это объектная оболочка вокруг определенного этапа выполнения программы внутри домена приложения.
Именование потока
Потоки рождаются безымянными. Это означает, что у объекта, представляющего поток, свойство Name имеет значение null. Ничего страшного. Главный поток все равно изначально поименовать некому. То же самое и с остальными потоками. Однако никто не может помешать потоку поинтересоваться своим именем — и получить имя. Несмотря на то, что это свойство при выполнении приложения играет вспомогательную роль, повторное переименование потоков недопустимо. Повторное изменение значения свойства Name приводит к возбуждению исключения.
using System;
using System.Threading;
namespace ThreadApp_1
{
class StartClass
{
static void Main(string[] args)
{//==============================================================
int i = 0;
bool isNamed = false;
do
{
try
{
if (Thread.CurrentThread.Name == null)
{
Console.Write("Get the name for current Thread > ");
Thread.CurrentThread.Name = Console.ReadLine();
}
else
{
Console.WriteLine("Current Thread : {0}.", Thread.CurrentThread.Name);
if (!isNamed)
{
Console.Write("Rename it. Please...");
Thread.CurrentThread.Name = Console.ReadLine();
}
}
}
catch (InvalidOperationException e)
{
Console.WriteLine("{0}:{1}",e,e.Message);
isNamed = true;
}
i++;
}
while (i < 10);
}//=============================================================
}
}
Листинг
15.2.
Игры с потоками
Но сначала о том, что должно происходить в потоке. Выполнение текущего потока предполагает выполнение программного кода.
Что это за код? Это код приложения. Это код статических и нестатических методов – членов классов, которые объявляются или просто используются в приложении.
Запустить поток можно единственным способом: указав точку входа потока – метод, к выполнению операторов которого должен приступить запускаемый поток.
Точкой входа ПЕРВИЧНОГО потока являются СТАТИЧЕСКИЕ функции Main или WinMain. Точнее, первый оператор метода.
Точка входа ВТОРИЧНОГО потока назначается при создании потока.
А дальше – как получится. Поток выполняется оператор за оператором. Со всеми циклами, заходами в вызываемые функции, в блоки свойств, в операторы конструкторов... И так продолжается до тех пор, пока:
- не возникнет неперехваченное исключение;
- не будет достигнут конец цепочки операторов (последний оператор в функциях Main или WinMain );
- не будет отработан вызов функции, обеспечивающей прекращение выполнения потока.
Поскольку первичный поток создается и запускается автоматически (без какого-либо особого участия со стороны программиста), то и заботиться в случае простого однопоточного приложения не о чем.
При создании многопоточного приложения забота о создании дополнительных потоков – это забота программиста. Здесь все надо делать своими руками.
Деятельность по созданию потока предполагает три этапа:
- определение метода, который будет играть роль точки входа в поток;
- создание объекта – представителя специального класса-делегата ( ThreadStart class ), который настраивается на точку входа в поток;
- создание объекта – представителя класса потока. При создании объекта потока конструктору потока передается в качестве параметра ссылка на делегата, настроенного на точку входа.
Замечание. Точкой входа в поток не может быть конструктор, поскольку не существует делегатов, которые могли бы настраиваться на конструкторы.