Потоки
А кто в домене живет?
Прилагаемый пример демонстрирует методы анализа процесса, домена и потоков (в .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 ), который настраивается на точку входа в поток;
- создание объекта – представителя класса потока. При создании объекта потока конструктору потока передается в качестве параметра ссылка на делегата, настроенного на точку входа.
Замечание. Точкой входа в поток не может быть конструктор, поскольку не существует делегатов, которые могли бы настраиваться на конструкторы.