Потоку.NET автоматически назначается апартамент при переходе к использующему апартаменты Win32-коду или унаследованному COM-коду. По умолчанию он помещается в многопоточный апартамент, если только не запрашивает однопоточный следующим образом:
Thread t = new Thread(...); t.SetApartmentState(ApartmentState.STA); |
Можно также запросить, чтобы главный поток приложения вошел в однопоточный апартамент, используя атрибут STAThread для метода main:
class Program { [STAThread] static void Main() { ... |
Апартаменты никак не влияют на исполнение чистого.NET-кода. Другими словами, два потока из STA могут одновременно вызвать один и тот же метод одного и того же объекта, и при этом не будет никакого маршалинга или блокировок. Это может случиться, только когда дело дойдет до неуправляемого кода.
Типы из пространства имен System.Windows.Forms интенсивно вызывают Win32-код, разработанный в расчете на работу в однопоточном апартаменте. По этой причине main-метод программы Windows Forms должен быть помечен как [STAThread], иначе при вызове Win32 UI-кода произойдет одно из двух:
|
|
§ Произойдет маршалинг к STA.
§ Все накроется медным тазом.
Control.Invoke
В многопоточном приложении Windows Forms запрещено вызывать методы и свойства элементов управления из потоков, отличных от того, в котором они были созданы. Для всех межпоточных вызовов должен быть явно выполнен маршалинг в поток, в котором был создан элемент управления с использованием методов Control.Invoke или Control.BeginInvoke. Нельзя полагаться на автоматический маршалинг, так как он происходит слишком поздно, когда дело уже дошло до неуправляемого кода, в то время как предшествующий.NET-код уже отработал в “неправильном” потоке – и такой код не будет потокобезопасным.
Превосходное решение для управления рабочими потоками в приложениях Windows Forms состоит в использовании BackgroundWorker. Этот класс-обертка для рабочих потоков умеет уведомлять о ходе выполнения операции и ее завершении, и автоматически вызывает Control.Invoke там, где это нужно.