Re: STAThread & MTAThread атрибуты
От: vmpire Россия  
Дата: 17.11.09 13:12
Оценка: 58 (9)
Здравствуйте, MozgC, Вы писали:

MC>Здравствуйте,

MC>Кто-нибудь может ДОХОДЧИВО, ПРОСТО И ПОДРОБНО объяснить про необходимость и последствия установки STA или MTA для потока? В частности наверное лучше в контексте WinForms.
MC>Ссылки на MSDN не нужно приводить, я читал и MSDN и еще в 10 местах в интернете, но, на мой взгляд, нигде это не описано так чтобы можно было прочитать и полностью все понять.
MC>Поэтому прошу тех, кто реально хорошо это понимает, объяснить на пальцах и с примерами.

Бес ссылок не обойдётся
Прежде всего, данное свойство потока влияет только на вызов COM — объектов. Объекты могут создаваться явно или с помощью .NET-обёрток (в том числе в стандартной библиотеке). Отсюда вывод: если приложение не использует СОМ ни непосредственно ни опосредованно (например, консольное приложение без явного обращения к СОМ), то установка свойства STA/MTA не имеет эффекта вообще.

Зачем нужно
Если СОМ используется, то среда .NET должна проинициализировать СОМ в каждом потоке, где он нужен, вызвав CoInitialize или CoInitializeEx с правильным аргументом (COINIT_MULTITHREADED или COINIT_APARTMENTTHREADED). Так как программист напрямую эти функции не вызывает, то нужно как-то передать информацию об этом в среду выполнения. Это и делается установкой атрибута STAThread/MTAThread или вызовом SetApartmentState.
Далее я буду говорить только про InProc-сервера, для OutOfProc есть отличия.

На что влияет
COM-объекты могут регистрироваться в системе по разному. Некоторые написаны руками, реентерабельны, и им всё равно, откуда и сколько раз одновременно их вызывают. Другие не реентерабельны и могут работать нормально только если все вызовы идут из одного потока. Таковы объекты. написанные на Visual Basic (по историческим причинам их очень много) или объекты, где программисты не заморачивались реэнтерабельностью, переложив контроль на среду выполнения. К сожалению, многие штатные библиотеки Windows (например, common controls, активно используемые .NET) также не реентерабельны.

Для различения таких типов объектов при их регистрации в системе используется свойство ThreadingModel. Если ThreadingModel=Apartment, то объект не реентерабельный и будет всегда вызываться только из того потока, в котором он создан. Все вызовы из других потоков при этом направляются объекту через сообщения Windows и обрабатываются по очереди. Если объект реентерабельный, то он помечается как ThreadingModel=Free, Both или Neutral (подробнее — смотрите в MSDN http://msdn.microsoft.com/en-us/library/ms681753(VS.85).aspx). При этом он может быть (не всегда, зависит от режима вызывающего потока) вызван напрямую даже из треда, который объект не создавал. При этом нет накладных расходов на сериализацию параметров вызова и результата вызова в windows message.

Некоторые выводы (не для всех комбинаций)
— Если Apartment-объект вызывается из потока, который его не создавал, имеем ощутимые накладные расходы на вызов независимо от режима потока
— Если Apartment-объект вызывается из STA-потока, который его создавал, накладных расходов нет
— Если Apartment-объект вызывается их MTA-потока, опять же имеем накладные расходы, так как объект будет создан в другом апартменте
— В многопоточной программе использование Apartment-объекта из разных потоков будет тормозить
— Если Free или Both объект вызывается из MTA-потока, накладных расходов нет, объект дёргается напрямую. Видимо, поэтому, начиная с .NET 2.0 режим потока по умолчанию — MTA.
— Так как многие UI-компоненты обычно зарегистрированы как Apartment, имеет смысл основной UI поток приложения помечать как STA для уменьшения накладных расходов и устранения глюков некоторых компонентов, которые по другому не умеют. При этом, правда, есть неприятное следствие: так как вышеупомянутые сообщения доставляются циклом обработки сообщений этого же потока, то если объект будет вызван из MTA-потока, то он не сможет вернуть результат (так как поток ждёт отправки результата, а она не произойдёт, пока поток не пошлёт сообщение, а он не пошлёт, так как ждйт отправки результата....), то есть поток зависнет.
— Если нужна работа без накладных расходов с Free или Both объектами, то поток стоит помечать как MTA, но лучше, если это не будет основной UI поток приложения по причинам, указанным выше.


Ссылки с подробностями:
http://msdn.microsoft.com/en-us/library/ms693421%28VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/ms680112%28VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/ms681753%28VS.85%29.aspx?ppud=4
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.