Знакомство
с многопоточностью
Каждая
программа работает в определенном контексте,
описывающем распределение кода и
данных в памяти. При сохранении
контекста фактически сохраняется
состояние программного потока, что
позволяет в будущем восстановить
его и продолжить выполнение
программы.
Сохранение
контекста сопряжено с
определенными затратами времени и
памяти. Операционная система
запоминает состояние программного
потока и передает управление
другому потоку. Когда программа
захочет продолжить выполнение
приостановленного потока,
сохраненный контекст приходится
восстанавливать, на что уходит еще
больше времени. Следовательно,
многопоточность следует
использовать лишь в тех случаях,
когда преимущества компенсируют
все затраты. Ниже перечислены
некоторые типичные примеры.
Прежде чем
переходить к механике работы
многопоточных программ, необходимо
указать на одно обстоятельство,
часто вызывающее недоразумения у
новичков в области многопоточного
программирования.
В программном
потоке выполнятся процедура, а не
объект.
Трудно сказать,
что следует понимать под
выражением «выполняется объект»,
но один из авторов часто ведет
семинары по многопоточному
программированию и этот вопрос
задают чаще других. Возможно, кто-то
полагает, что работа программного
потока начинается с вызова метода
New класса, после чего поток
обрабатывает все сообщения,
передаваемые соответствующему
объекту. Такие представления абсолютно
неверны. Один объект может
содержать несколько потоков,
выполняющих разные (а иногда даже
одинаковые) методы, при этом
сообщения объекта передаются и
принимаются несколькими разными
потоками (кстати, это одна из причин,
затрудняющих многопоточное
программирование: чтобы отладить
программу, необходимо узнать, какой
поток в данный момент выполняет ту
или иную процедуру!).
Поскольку
программные потоки создаются на
базе методов объектов, сам объект
обычно создается раньше потока.
После успешного создания объекта
программа создает поток, передавая
ему адрес метода объекта, и только
после этого отдает распоряжение
о начале выполнения потока.
Процедура, для которой создавался
поток, как и все процедуры, может
создавать новые объекты, выполнять
операции с существующими объектами
и вызывать другие процедуры и
функции, находящиеся в ее области
видимости.
В
программных потоках также могут
выполняться общие методы классов. В
этом слу-Также помните о другом
важном обстоятельстве: поток
завершается с выходом из процедуры,
для которой он был создан. До выхода
из процедуры нормальное завершение
программного потока невозможно.
Потоки
могут завершаться не только
естественно, но и аварийно. Обычно
делать это не рекомендуется. За
дополнительной информацией
обращайтесь к разделу «Завершение
и прерывание потоков».
Основные
средства .NET, относящиеся к
использованию программных потоков,
сосредоточены в пространстве имен
Threading. Следовательно, большинство
многопоточных программ должно
начинаться со следующей строки:
Imports System.Threading
Импортирование
пространства имен упрощает ввод
программы и позволяет использовать
технологию IntelliSense.
Непосредственная связь потоков с процедурами наводит на предположение о том, что в этой картине важное место занимают делегаты (см. главу 6). В частности, в пространство имен Threading входит делегат ThreadStart, обычно используемый при запуске программных потоков. Синтаксис использования этого делегата выглядит так:
Public Delegate Sub
ThreadStart()
Код, вызываемый
при помощи делегата ThreadStart, не
должен иметь параметров и
возвращаемого значения, поэтому
потоки не могут создаваться для
функций (которые возвращают
значение) и для процедур с
параметрами. Для передачи
информации из потока тоже
приходится искать альтернативные
средства, поскольку выполняемые
методы не возвращают значений и не
могут использовать передачу по
ссылке. Например, если процедура
ThreadMethod находится в классе WilluseThread,
то ThreadMethod может передавать
информацию посредством изменения
свойств экземпляров класса WillUseThread.
Программные
потоки .NET работают в так называемых
доменах приложений, определяемых в
документации как «изолированная
среда, в которой выполняется
приложение». Домен приложения
можно рассматривать как
облегченный вариант процессов Win32;
один процесс Win32 может содержать
несколько доменов приложений.
Главное отличие между доменами
приложений и процессами
заключается в том, что процесс Win32
обладает самостоятельным адресным
пространством (в документации
домены приложений также
сравниваются с логическими
процессами, работающими внутри
физического процесса). В .NET все
управление памятью осуществляется
исполнительной средой, поэтому в
одном процессе Win32 могут работать
несколько доменов приложений.
Одним из преимуществ этой схемы
является улучшение возможностей
масштабирования (scaling) приложений.
Средства для работы с доменами
приложений находятся в классе AppDomain.
Рекомендуем изучить документацию
по этому классу. С его помощью можно
получить информацию об окружении, в
котором работает ваша программа. В
частности, класс AppDomain применяется
при выполнении рефлексии для
системных классов .NET. Следующая
программа выводит список
загруженных сборок.
Imports System.Reflection
Module Modulel
Sub Main()
Dim theDomain As
AppDomain
theDomain =
AppDomain.CurrentDomain
Dim Assemblies()As [Assembly
]
Assemblies = theDomain.GetAssemblies
Dim anAssemblyxAs [Assembly ]
For Each anAssembly
In Assemblies
Console.WriteLinetanAssembly.Full
Name) Next
Console.ReadLine()
End Sub
End Module