Пространство имен System.Threading появилось в версии .NET 1.0 и предлагает один из подходов к построению многопоточных приложений. Равным типом в этом пространстве имен можно назвать, пожалуй, класс Thread, поскольку он представляет отдельный поток. Если необходимо программно получить ссылку на поток, который в текущий момент выполняет заданный член, то нужно просто обратиться к статическому свойству Thread.CurrentThread:
static void ExtractExecutingThread()
{
// Получить поток, который в настоящий момент выполняет данный метод.
Thread currThread = Thread.CurrentThread;
}
Вспомните, что в .NET Core существует только один домен приложения. Хотя создавать дополнительные домены приложений нельзя, домен приложения может иметь многочисленные потоки, выполняющиеся в каждый конкретный момент времени. Чтобы получить ссылку на домен приложения, который обслуживает приложение, понадобится вызвать статический метод Thread.GetDomain():
static void ExtractAppDomainHostingThread()
{
// Получить домен приложения, обслуживающий текущий поток.
AppDomain ad = Thread.GetDomain();
}
Одиночный поток в любой момент также может быть перенесен в контекст выполнения и перемещаться внутри нового контекста выполнения по прихоти среды .NET Core Runtime. Для получения текущего контекста выполнения, в котором выполняется поток, используется статическое свойство Thread.CurrentThread.ExecutionContext:
static void ExtractCurrentThreadExecutionContext()
{
// Получить контекст выполнения, в котором работает текущий поток.
ExecutionContext ctx =
Thread.CurrentThread.ExecutionContext;
}
Еще раз: за перемещение потоков в контекст выполнения и из него отвечает среда .NET Core Runtime. Как разработчик приложений .NET Core, вы всегда остаетесь в блаженном неведении относительно того, где завершается каждый конкретный поток. Тем не менее, вы должны быть осведомлены о разнообразных способах получения лежащих в основе примитивов.
Сложность, связанная с параллелизмом
Один из многих болезненных аспектов многопоточного программирования связан с ограниченным контролем над тем, как операционная система или исполняющая среда задействует потоки. Например, написав блок кода, который создает новый поток выполнения, нельзя гарантировать, что этот поток запустится немедленно. Взамен такой код только инструктирует операционную систему или исполняющую среду о необходимости как можно более скорого запуска потока (что обычно происходит, когда планировщик потоков добирается до него).
Кроме того, учитывая, что потоки могут перемещаться между границами приложений и контекстов, как требуется исполняющей среде, вы должны представлять, какие аспекты приложения являются
Чтобы проиллюстрировать проблему, давайте предположим, что поток вызывает метод специфичного объекта. Теперь представим, что поток приостановлен планировщиком потока, чтобы позволить другому потоку обратиться к тому же методу того же самого объекта.
Если исходный поток не завершил свою операцию, тогда второй входящий поток может увидеть объект в частично модифицированном состоянии. В таком случае второй поток по существу читает фиктивные данные, что определенно может привести к очень странным (и трудно обнаруживаемым) ошибкам, которые еще труднее воспроизвести и устранить.
С другой стороны, атомарные операции в многопоточной среде всегда безопасны. К сожалению, в библиотеках базовых классов .NET Core есть лишь несколько гарантированно атомарных операций. Даже действие по присваиванию значения переменной-члену не является атомарным! Если только в документации по .NET Core специально не сказано об атомарности операции, то вы обязаны считать ее изменчивой в потоках и предпринимать соответствующие меры предосторожности.
Роль синхронизации потоков
К настоящему моменту должно быть ясно, что многопоточные программы сами по себе довольно изменчивы, т.к. многочисленные потоки могут оперировать разделяемыми ресурсами (более или менее) одновременно. Чтобы защитить ресурсы приложений от возможного повреждения, разработчики приложений .NET Core должны применять потоковые примитивы (такие как блокировки, мониторы, атрибут [Synchronization] или поддержка языковых ключевых слов) для управления доступом между выполняющимися потоками.