Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.02.06;
Скачать: CL | DM;

Вниз

Потоковые модели и COM   Найти похожие ветки 

 
Тимохов ©   (2004-01-23 16:32) [0]

Уважаемые мастера Дельфи, имеющие опыт в работе с COM.

Несмотря на то, что давно использую COM в своей работе, решил тут лучше изучить загадоченые (для меня) правила работы с потоками в COM.

Начал с http://podgoretsky.com/ftp/Docs/Delphi/DX/COMmodel.html
После чего почитал (правда, бегло) MSDN по этому вопросу.

Беглось вызвана базовым непониманием некоторых вопросов, некоторые из которых я приведу ниже.

Сразу скажу, что данная тема интересует меня только с точки зрения разработки клиентского приложения и использования inproc сервера с моделью Apartment. В дальнейшем, обязательно, изучу другие потоковые модели.

Заранее благодарен за ответы.

Вопрос 1. Есть следующий код.
// thread
type
TMyThread = class(TThread)
procedure Execute; override;
end;

procedure TMyThread.Execute();
begin
...
end;

// main programm
procedure TForm1.Button1OnClick(...);
var
t: TMyThread;
begin
t := TMyThread.Create(false);
end;


Скажите, пожалуйста, сколько в такой программе существует STA: два (в одном главный поток, в другом поток TMyThread) или один (содержащий оба потока)?

Остальные вопросы. Задам в зависимости от ответа на первый.


 
Digitman ©   (2004-01-23 16:38) [1]


> STA


переведи)..


 
Тимохов ©   (2004-01-23 16:39) [2]

STA
single-threa ted apartment

или

single-threa ded apartment


 
Opuhshii ©   (2004-01-23 16:40) [3]

в такой программе STA?...
нисколько,..


 
Тимохов ©   (2004-01-23 16:42) [4]


> в такой программе STA?...
> нисколько,..

Уверен?
CoInitialize в system вызывается (InitProc).
Модуль ComObj подключен (виноват, не написал ранее).


 
Digitman ©   (2004-01-23 16:44) [5]

кем ? тобой в ЭТОМ потоке не вызывается точно ... не видно это из твоего кода


 
Тимохов ©   (2004-01-23 16:44) [6]

Digitman © (23.01.04 16:44) [5]
См. [4].
Разве это не гарантирует вызова CoInitialize, который, насколько я понимаю, и делает STA.


 
Digitman ©   (2004-01-23 16:48) [7]

нет, не гарантирует

автоматом его никто за тебя делать не будет

автоматом его вызывает лишь Борланд для осн.код.потока при использовании соотв.модулей VCL

для прочих код.потоков это будет на твоей совести


 
Тимохов ©   (2004-01-23 16:53) [8]

Digitman © (23.01.04 16:48) [7]
Т.е. я так понимаю, что:
1. основной поток у меня в STA (т.к. вызвался InitProc, переопределенный в ComObj на вызов CoInitialize).
2. поток TMyThread вообще нигде, т.е. не в STA.

Правильно?


 
Digitman ©   (2004-01-23 17:00) [9]

думаю, что CoInitizlize() не имеет непоср, отношения к механизму thread apartment в принципе


 
Тимохов ©   (2004-01-23 17:02) [10]


> думаю, что CoInitizlize() не имеет непоср, отношения к
> механизму thread apartment в принципе

Врядли, т.к. в CoInitializeEx явно задается поточкая модель.


 
Digitman ©   (2004-01-23 17:04) [11]

где ? покажи ...
ссылка на док-цию ?


 
Тимохов ©   (2004-01-23 17:05) [12]

Digitman © (23.01.04 17:04) [11]

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/htm/cmf_a2c_5iyg.asp


 
Digitman ©   (2004-01-23 17:16) [13]

ясно.

а далее, в соотв-вии с док-том, все зависит от Threading Model того объекта, к которому будут осуществляться обращения из данногго и/или иного код.потока


 
Тимохов ©   (2004-01-23 17:33) [14]


> а далее, в соотв-вии с док-том, все зависит от Threading
> Model того объекта, к которому будут осуществляться обращения
> из данногго и/или иного код.потока

Я вроде понял, что не совсем так.
Важна комбинация модели сервера + модель клиента + типа сервера (inproc или внешний).
Темный тут все-таки лес.
Поясню, почему возникла вообще потребность в этом разобраться.

Есть проект. В главном потоке создается AdoDb.Connection (через имортированную библиотеку типов). Есть поток, который юзает этот conntion без всякого маршалинга, чтобы раз в 10 мин обращаться к серверу. Я не устанавливаю модель потоков для клента (т.е. она соглавно описанию OnInitialize равна apartment). AdoDb имет потоковую модель apartment (согласно реестру).

Все прекрасно работает не первый год. Кто-то мне сказал, что так делать нельзя. Полез в документацию, и прочел, что к com объекту, созданному в одном STA, можно обращаться из другого STA только через маршалинг. Из того же STA можно без маршалинга. При том, если этого не делать, то я обязательно должен получить ошибку. Но я это делаю, и все работает. Т.е. получается, что главнй поток и дополнительный в одном STA.

Вот такая логика.

Но. В этом случае я совершенно не понимаю как перевести следующую фразу из MSDN "Single-threaded Apartments—Single-threaded apartments consist of exactly one thread, ". У меня потока то два :(((

ЗЫ: ясно что здесь без АП не разберешься.


 
Бином Ньютоныч   (2004-01-23 18:15) [15]

1)CoInitializeEx.

Эта функция инициирует для вызвавшего ее потока подсистему COM. В том числе она определяет, в какой апартамент будет включен данный поток. Если заявлен STA, то будет создан новый апартамент, так как в STA может жить только один поток. MTA в приложении может быть только один, поэтому все потоки, запросившие эту модель, будут выполняться в одном апартаменте.

2)Зачем апартаментные модели объектам.

Апартаментная модель существует для того, чтобы подсистема COM могла оказать помощь программисту в обепечении безопасности вызовов методов одного объекта из разных потоков. Если объект зарегистрирован с моделью MTA, то это значит, что его автор позаботился об этой безопасности и помощи от COM не требуется. Если объект заявлен с моделью STA, то вызывать его методы из разных потоков одновременно небезопасно и вызовы нужно сериализовать. Такой взгляд не охватывает все нюансы, но для начала сойдет.

3)Передача интерфейсов между апартаментами.

СОМ МОЖЕТ помочь программисту в сериализации вызовов, но об этом нужно ее попросить. Эта просьба выражается в использовании механизма маршаллинга. Если просто передать ссылку на интерфейс из одного апартамента в другой, то СОМ не будет вмешиваться, это чисто твоя ответственность. И это вполне может оказаться безопасным и не привести ни к каким ошибкам, если, например, не работать с одним интерфейсом сразу в нескольких потоках. В твоем случае видимо так и происходит. Но если ты попытаешься использовать это соединение в разных потоках, то наверняка получишь ошибки из-за одновременного обращения из разных потоков к внутренним данным объекта, реализующего интерфейс, или к каким-то неразделяемым ресурсам, которые этот объект импользует.


 
Тимохов ©   (2004-01-23 18:22) [16]

Бином Ньютоныч (23.01.04 18:15) [15]


> СОМ МОЖЕТ помочь программисту в сериализации вызовов, но
> об этом нужно ее попросить. Эта просьба выражается в использовании
> механизма маршаллинга. Если просто передать ссылку на интерфейс
> из одного апартамента в другой, то СОМ не будет вмешиваться,
> это чисто твоя ответственность. И это вполне может оказаться
> безопасным и не привести ни к каким ошибкам, если, например,
> не работать с одним интерфейсом сразу в нескольких потоках.
> В твоем случае видимо так и происходит. Но если ты попытаешься
> использовать это соединение в разных потоках, то наверняка
> получишь ошибки из-за одновременного обращения из разных
> потоков к внутренним данным объекта, реализующего интерфейс,
> или к каким-то неразделяемым ресурсам, которые этот объект
> импользует.


Огромное спасибо, что не пожалели времени на такой ответ :)))
Вопросы-уточнения.

По поводу пункта 1)
Я не понимаю, в каком аппартменте находится подчиненный поток (я не вызываю явно CoInitialize него)?

По поводу пункта 3)
Я работаю с одним интерфейсом в несокольких потоках. Но!
У меня обращение к connection защищено критической секцией.
Наверное, по этому и не происходит ничего срашного. Если критическую секцию убрать, то может быть кырдык. Правильно я понимаю?


 
Ломброзо ©   (2004-01-23 18:34) [17]


> У меня обращение к connection защищено критической секцией


Угу, ничего страшного. Но изобилие критических секций сводит на нет все преимущества MTA.


 
Тимохов ©   (2004-01-23 18:41) [18]

Во, теперь MTA всплыло :(((
Все-таки не могу добиться понимания, что такое аппартмент вообще.
Насколько я понимаю, MTA это НЕ синоним многопоточное приложение. STA, соответственно, не синоним однопоточного.

Итак, вопрос остатется: сколько в приведенном примере клиентского приложения аппартменов (при условии того, что SomeComObject это внутренний apparment сервер)?

// thread
type
TMyThread = class(TThread)
procedure Execute; override;
end;

procedure TMyThread.Execute();
begin
...
o.SomeMethod;
...
end;

// main programm
uses
ComObj;
var
o: ISomeComObject;
procedure TForm1.Button1OnClick(...);
var
t: TMyThread;
begin
o := CreateComObject(...);
t := TMyThread.Create(false);
end;


 
Бином Ньютоныч   (2004-01-23 18:42) [19]

>Я не понимаю, в каком аппартменте находится подчиненный поток (я не вызываю явно CoInitialize него)?

Ни в каком. Подсистема COM для него просто не проинициировна и если ты попытаешься обратиться к ее сервисам, то сразу получишь отлуп. Это относится, например, к механизму маршалинга, защиты, канальным хукам и тому подобным вкусностям:)


 
Тимохов ©   (2004-01-23 18:45) [20]


> Ни в каком. Подсистема COM для него просто не проинициировна
> и если ты попытаешься обратиться к ее сервисам, то сразу
> получишь отлуп. Это относится, например, к механизму маршалинга,
> защиты, канальным хукам и тому подобным вкусностям:)

То что дополнительный поток не относится ни к какому аппартменту, я понял. Спасибо.

А то, что у меня все работает объяснимо только критическими секциями при обращении к объекту, созданному в основном потоке. Правильно?


 
Ломброзо ©   (2004-01-23 18:49) [21]

Здесь - один, главный (если включен ComObj). Под connection у тебя ADOConnection разумеется? если STA, то это гарантия того, что у тебя этот указатель на этот интерфейс будут маршалиться в другой апартмент. Поток TMyThread же у тебя CoInitialize не вызывал, потому имхо обломится.


 
Тимохов ©   (2004-01-23 18:51) [22]


> Поток TMyThread же у тебя CoInitialize не вызывал, потому
> имхо обломится.

Вот! В этом и корень вопроса. Поток работает. И работает хорошо - несмотря на активную эксплуатацию в течении 3 лет, ни разу не было проблем.
Вот за что я не люблю COM - все прочел, все понял, а работает не так как написано: либо не работает, когда должно, либо работает, когда не должно.

:((((


 
Ломброзо ©   (2004-01-23 18:55) [23]

А скажи пожалуйста, с чем ты действительно работаешь? с ADOInt._Connection или TADOConnection?


 
Тимохов ©   (2004-01-23 18:57) [24]

fAdoMainCnn := ADODB.CoConnection.Create, где ADODB модуль, созданный дельфи при импорте библиотеки типов Microsort ADODB ...


 
Ломброзо ©   (2004-01-23 19:06) [25]

Сдаётся мне, что всё дело в том, что ты ведь включаешь ADODB_TLB.pas в модуле потока, а этот модуль включает в себя ComObj. Иначе бы ты получил эксепшн "Не был произведён вызов CoInitialize".


 
Бином Ньютоныч   (2004-01-23 19:06) [26]

>Тимохов © (23.01.04 18:41) [18]

Апартамент - это абстракция, некоторый набор взаимосвязанных правил взаимодействия объектов. Допустим у тебя есть объект в DLL с моделью STA. Если ты попытаешься создать такой объект посредством стандартных механизмов COM из потока в STA, то этот объект будет создан в этом вызывающем потоке. То бишь метод CreateInstance его фабрики будет вызван в контексте этого потока. Если же создавать из потока в MTA, то СОМ запустит новый поток, проинициирует для него STA, и в нем произведет вызов CreateInstance. Это и есть применение правил межапартаментного взаимодействия. Но если ты сам получишь указатель на IClassFactory или любой другой интерфейс объекта класса, и просто вызовешь метод создания объекта, то СОМ не сможет вмешаться и объект будет создан в контексте того потока, откуда ты захотел. То же самое при передаче интерфейса между апартаментами. Если использовать маршалинг, то при демаршалинге СОМ определит, что импорт производится в иной апартамент, нежели апартамент-экспортер. В этом случае в импортере будет создан прокси, и его интерфейс вернет Counmarshalinterface. А если, как в примере, просто передать ссылку, то СОМ не вмешается. Это будет обыкновенное присваивание одной переменной значения другой. По сути ничем не отличается от такого:
var
a, b: integer;
...
b:=a;
Никакой разницы.

> (при условии того, что SomeComObject это внутренний apparment сервер)?
Вероятно, это оговорка. Объект и апартамент - совершенно разные понятия, смотри выше.


 
Ломброзо ©   (2004-01-23 19:08) [27]

попробуй создать _Connection через CoCreateInstance и обратиться к нему в главном потоке (в каком-нить ButtonClick) без предварительного вызова CoInitialize.


 
Ломброзо ©   (2004-01-23 19:10) [28]

не просто создать, а вызвать какой-нить метод


 
Тимохов ©   (2004-01-23 19:20) [29]


> Ломброзо © (23.01.04 19:06) [25]
> Сдаётся мне, что всё дело в том, что ты ведь включаешь ADODB_TLB.pas
> в модуле потока, а этот модуль включает в себя ComObj. Иначе
> бы ты получил эксепшн "Не был произведён вызов CoInitialize".

Здесь, я думаю, что Вы не совсем правы: если в модуль потока включен ComObj, это не значит, что для потока вызван метод OnInitialize. Он вызывается только если это сделать явно в Execute.

Бином Ньютоныч (23.01.04 19:06) [26]
Еще раз спасибо.


> > (при условии того, что SomeComObject это внутренний apparment
> сервер)?
> Вероятно, это оговорка. Объект и апартамент - совершенно
> разные понятия, смотри выше.

Имелось в виду, это inproc server с моделью appartment, которая прописана в реестре.

Не буду Вас утруждать более вопросами, кроме одного - ответ на него даст мне пищу для размышлений.
Будет ли в приведенном ниже примере создано два STA аппартамента для главного потока и для подчиненного:

// thread
type
TMyThread = class(TThread)
procedure Execute; override;
end;

procedure TMyThread.Execute();
begin
CoInitialize(nil);
... // do something
CoUnInitialize();
end;

// main programm
uses
ComObj;
procedure TForm1.Button1OnClick(...);
var
t: TMyThread;
begin
t := TMyThread.Create(false);
end;


Понятно, что, наверное, ответ на данный вопрос я уже мог получить из ответов в данной ветке. Просто хочу еще раз услышать короткий ответ и продолжить изучение этой темы, отталкиваясь от полученных знаний :)))
Заранее спасибо.


 
Бином Ньютоныч   (2004-01-23 19:30) [30]

>Будет ли в приведенном ниже примере создано два STA >аппартамента для главного потока и для подчиненного

Несомненно:) Хотя, конечно, корректнее все же проверять результат вызова CoInitialize..ну так, от греха подальше:)


 
Тимохов ©   (2004-01-23 19:30) [31]

Бином Ньютоныч (23.01.04 19:30) [30]
Большое спасибо за ответы.
Буду дальше изучать...



Страницы: 1 вся ветка

Текущий архив: 2004.02.06;
Скачать: CL | DM;

Наверх




Память: 0.56 MB
Время: 0.018 c
3-16054
Yuri I. Bouloui
2004-01-12 16:16
2004.02.06
TClientDataSet -> TDataSetProvider -> TIBDataSet. Получить ПК?


11-16202
Евгений
2003-05-19 20:02
2004.02.06
CopyFrom


14-16622
LSV
2004-01-15 03:16
2004.02.06
Подскажите, какие книги по Delphi 7 самые лучшие?


1-16285
MakNik
2004-01-26 09:27
2004.02.06
Всплывающие подсказки как у Windows Messenger-а


3-16150
Veetyok
2004-01-14 00:49
2004.02.06
INSERT INTO