Чтение онлайн

ЖАНРЫ

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание

Троелсен Эндрю

Шрифт:

Кроме того, каждый домен приложения может содержать любое число контекстов. Используя этот дополнительный уровень изоляции типов, среда CLR может гарантировать, что объекты со специальными требованиями будут обработаны корректно. Глава завершается рассмотрением деталей того, как сама CLR обрабатывается операционной системой Win32.

ГЛАВА 14. Создание многопоточных приложений

В предыдущей главе мы рассмотрели взаимосвязь между процессами, доменами приложения и контекстами. В этой мы выясним, как в рамках платформы .NET строить многопоточные приложения и как в условиях множества потоков гарантировать целостность

совместно используемых ресурсов.

Наше обсуждение снова начнется с рассмотрении типа делегата .NET, чтобы прийти к пониманию его внутренней поддержки асинхронных вызовов методов. Вы увидите, что такой подход позволяет автоматически вызвать метод во вторичном потоке выполнения. Затем мы исследуем типы пространства имен System.Тhreading. Будет рассмотрено множество типов (Thread.ThreadStart и т.д.), позволяющих с легкостью создавать дополнительные потоки. Конечно, сложность разработки многопоточных приложений заключается не в создании потоков, а в гарантии того, что ваш программный код будет иметь надежные средства обработки конфликтов при конкурентном доступе к общедоступным ресурсам. Поэтому завершается глава рассмотрением различных примитивов синхронизации, предлагаемых каркасом .NET Framework.

Взаимосвязь процессов, доменов приложений, контекстов и потоков

В предыдущей главе обсуждалось понятие потока, который был определен, как путь исполнения в рамках выполняемого приложения. И хотя многие приложения .NET имеют только один поток и, тем не менее, оказываются очень полезными, первичный поток компоновочного блока (порождаемый средой CLR при выполнении Main) может создавать вторичные потоки для решения дополнительных задач. Реализуя дополнительные потоки, вы можете строить приложения с лучшим откликом на действия пользователя (но не обязательно более быстро выполняющие свои задачи).

Пространство имен System.Threading содержит различные типы, позволяющие создавать многопоточные приложения. Основным типом здесь можно считать класс Thread, поскольку он представляет данный поток. Чтобы программно получить ссылку на поток, выполняющий данный член в текущий момент, просто вызовите статическое свойство Thread.CurrentThread.

private static void ExtractExecutingThread {

 // Получение потока, выполняющего

 // в данный момент данный метод.

Thread currThread = Thread.CurrentThread;

}

Для платформы .NET не предполагается прямого однозначного соответствия между доменами приложения и потоками. Напротив, домен приложения может иметь множество потоков, выполняющихся в рамках этого домена в любой момент времени. Кроме того, конкретный поток не привязан к одному домену приложения в течение всего времени существования потока. Потоки могут пересекать границы домена приложения, подчиняясь правилам потоков Win32 и целесообразности CLR.

Но, хотя активные потоки могут перемещаться через границы доменов приложения, в любой конкретный момент времени один конкретный поток может выполняться в рамках только одного домена приложения (другими словами, один поток не может работать в нескольких доменах приложения одновременно). Чтобы программно получить доступ к домену приложения, содержащему текущий поток, следует вызвать статический метод Thread.GetDomain.

private static void ExtractAppDomainHostingThread {

 //
Получение домена приложения, содержащего текущий поток.

 AppDomain ad = Thread.GetDomain;

}

Любой поток в любой момент времени также может быть перемещен средой CLR в любой из имеющихся контекстов или помещен в новый контекст. Чтобы получить текущий контекст, в рамках которого оказался поток, используйте статическое свойство Thread.CurrentContext.

private static void ExtractCurrentThreadContext {

 // Получение контекста, в рамках которого

 // действует текущий поток.

Context ctx = Thread.CurrentContext;

}

Снова подчеркнем, что именно среда CLR является тем объектом, который отвечает за помещение потоков в соответствующие домены приложения и контексты. Как разработчик приложений .NET, вы обычно остаетесь в блаженном неведении относительно того, где заканчивается данный поток (или, точнее, когда он помещается в новые границы). Однако вам будет полезно знать различные способы получения соответствующих примитивов.

Проблема конкуренции и роль синхронизации потоков

Одним из множества "преимуществ" (читайте источников проблем) многопоточного программирования является то, что вы имеете очень узкие возможности контроля в отношении использования потоков операционной системой и средой CLR. Например, построив блок программного кода, создающий новый поток выполнения, вы не можете гарантировать, что этот поток начнет выполняться немедленно. Скорее, такой программный код только "даст инструкцию" операционной системе начать выполнение потока как можно быстрее (что обычно означает момент, когда наступит очередь этого потока у планировщика потоков).

Кроме того, поскольку потоки могут перемещаться между границами приложения и контекста по требованию CLR, вы должны, следить за тем, какие элементы вашего приложения открыты влиянию потоков (т.е. позволяют доступ множества потоков), а какие операции оказываются атомарными (операции, открытые для множества потоков, потенциально опасны!). Для примера предположим, что поток вызывает некоторый метод конкретного объекта. Предположим также, что после этого поток, получает инструкцию от планировщика потоков приостановить выполнение, чтобы позволить другому потоку доступ к тому же методу того же объекта.

Если оригинальный поток еще не завершил свою текущую операцию, второй входящий поток может получить дли просмотра объект в частично измененном состоянии. В этом случае второй поток, по сути, будет читать некорректные данные, в результате чего возникнут досадные (и очень трудные для выявления) ошибки, которые характеризуются неустойчивостью при воспроизведении и отладке.

Атомарные операции, с другой стороны, всегда безопасны в многопоточном окружении. К сожалению, только для очень небольшого числа операций из библиотек базовых классов .NET можно гарантировать, что эти операции будут атомарными. Не является атомарной даже операция присваивание значения члену-переменной! Если в документации .NET Framework 2.0 SDK в отношении какой-либо операции специально не оговорено, что данная операция является атомарной, вы должны предполагать, что эта операция является открытой влиянию потоков и принимать специальные меры предосторожности.

Поделиться с друзьями: