К настоящему моменту должно быть ясно, что многопоточные программы сами по себе довольно изменчивы, т.к. многочисленные потоки могут оперировать разделяемыми ресурсами (более или менее) одновременно. Чтобы защитить ресурсы приложений от возможного повреждения, разработчики приложений .NET Core должны применять потоковые примитивы (такие как блокировки, мониторы, атрибут
[Synchronization]
или поддержка языковых ключевых слов) для управления доступом между выполняющимися потоками.
Несмотря на то что платформа .NET Core не способна полностью скрыть сложности, связанные с построением надежных многопоточных приложений, сам процесс был значительно упрощен. Используя типы из пространства
имен
System.Threading
, библиотеку TPL и ключевые слова
async
и
await
языка С#, можно работать с множеством потоков, прикладывая минимальные усилия.
Прежде чем погрузиться в детали пространства имен
System.Threading
, библиотеки TPL и ключевых слов
async
и
await
языка С#, мы начнем с выяснения того, каким образом можно применять тип делегата .NET Core для вызова метода в асинхронной манере. Хотя вполне справедливо утверждать, что с выходом версии .NET 4.6 ключевые слова
async
и
await
предлагают более простую альтернативу асинхронным делегатам, по-прежнему важно знать способы взаимодействия с кодом, использующим этот подход (в производственной среде имеется масса кода, в котором применяются асинхронные делегаты).
Пространство имен System.Threading
В рамках платформ .NET и .NET Core пространство имен
System.Threading
предоставляет типы, которые дают возможность напрямую конструировать многопоточные приложения. В дополнение к типам, позволяющим взаимодействовать с потоком .NET Core Runtime, в
System.Threading
определены типы, которые открывают доступ к пулу потоков, обслуживаемому .NET Core Runtime, простому (не связанному с графическим пользовательским интерфейсом) классу
Timer
и многочисленным типам, применяемым для синхронизированного доступа к разделяемым ресурсам.
В табл. 15.1 перечислены некоторые важные члены пространства имен
System.Threading
. (За полными сведениями обращайтесь в документацию по .NET Core.)
Класс System.Threading.Thread
Класс
Thread
является самым элементарным из всех типов в пространстве имен
System.Threading
. Он представляет объектно-ориентированную оболочку вокруг заданного пути выполнения внутри отдельного домена приложения. В этом классе определено несколько методов (статических и уровня экземпляра), которые позволяют создавать новые потоки внутри текущего домена приложения, а также приостанавливать, останавливать и уничтожать указанный поток. Список основных статических членов приведен в табл. 15.2.
Класс
Thread
также поддерживает члены уровня экземпляра, часть которых описана в табл. 15.3.
На заметку! Прекращение работы или приостановка активного потока обычно считается плохой идеей. В таком случае есть шанс (хотя и небольшой), что поток может допустить "утечку" своей рабочей нагрузки.
Получение статистических данных о текущем потоке выполнения
Вспомните, что точка входа исполняемой сборки (т.е. операторы верхнего уровня или метод
Main
) запускается в первичном потоке выполнения.
Чтобы проиллюстрировать базовое применение типа
Thread
, предположим, что имеется новый проект консольного приложения по имени
ThreadStats
. Как вам известно, статическое свойство
Thread.CurrentThread
извлекает объект
Thread
, который представляет поток, выполняющийся в текущий момент. Получив текущий поток, можно вывести разнообразные статистические сведения о нем:
// Не забудьте импортировать пространство имен System.Threading.
не было установлено, тогда будет возвращаться пустая строка. Однако назначение конкретному объекту
Thread
дружественного имени может значительно упростить отладку. Во время сеанса отладки в Visual Studio можно открыть окно Threads (Потоки), выбрав пункт меню Debug?Windows?Threads (Отладка? Окна?Потоки). На рис. 15.1 легко заметить, что окно Threads позволяет быстро идентифицировать поток, который нужно диагностировать.
Свойство Priority
Далее обратите внимание, что в типе
Thread
определено свойство по имени
Priority
. По умолчанию все потоки имеют уровень приоритета
Normal
. Тем не менее, в любой момент жизненного цикла потока его можно изменить, используя свойство