Почему нам необходим этот идентификатор?




Потому что, чтобы твердо придерживаться соглашения COM о потоках, мы должны создать промежуточный объект (proxy object) для объекта clsBackground. Промежуточный объект должен быть передан новому потоку вместо первоначального объекта. Обращения к новому потоку на промежуточном объекте будут переадресованы (marshaled) в текущий поток.

CoMarshalInterThreadInterfaceInStream выполняет интересную задачу. Она собирает всю информацию, необходимую при создании промежуточного объекта, для определенного интерфейса и загружает ее в объект потока (stream object). В этом примере мы используем интерфейс IDispatch, потому что мы знаем, что каждый класс Visual Basic поддерживает IDispatch и мы знаем, что поддержка переадресации (marshalling) IDispatch встроена в Windows - так что этот код будет работать всегда. Затем мы передаем объект потока (stream object) новому потоку. Этот объект разработан Windows, чтобы быть передаваемым между потоками одинаковым способом, так что мы можем безопасно передавать его функции CreateThread. Остальная часть функции StartBackgroundThreadApt идентична функции StartBackgroundThreadFree.

Функция BackgroundFuncApt также сложнее чем ее эквивалент при использовании модели свободных потоков и показана ниже:

' A correctly marshaled apartment model callback.

' This is the correct approach, though slower.

Public Function BackgroundFuncApt(ByVal param As Long) As Long

Dim qobj As Object

Dim qobj2 As clsBackground

Dim res&

' This new thread is a new apartment, we must

' initialize OLE for this apartment

' (VB doesn't seem to do it)

res = CoInitialize(0)

' Proper apartment modeled approach

res = CoGetInterfaceAndReleaseStream(param, IID_IDispatch, qobj)

Set qobj2 = qobj

Do While Not qobj2.DoTheCount(10000)

Loop

qobj2.ShowAForm

' Alternatively, you can put a wait function here,

' then call the qobj function when the wait is satisfied ' All calls to CoInitialize must be balanced

CoUninitialize

End Function

Первый шаг должен инициализировать подсистему OLE для нового потока. Это необходимо для переадресации (marshalling) кода, чтобы работать корректно. CoGetInterfaceAndReleaseStream создает промежуточный объект для объекта clsBackground и реализует объект потока (stream object), используемый для передачи данных из другого потока. Интерфейс IDispatch для нового объекта загружается в переменную qobj. Теперь возможно получить другие интерфейсы - промежуточный объект будет корректно переадресовывать данные для каждого интерфейса, который может поддерживать.

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

Хорошим результатом применения этого подхода является то, что все работает правильно. Объект clsBackground может безопасно показывать формы и генерировать события. Недостатком этого подхода является, конечно, его более медленное исполнение. Переключение потоков и переадресация (marshalling) - относительно медленные операции. Вы фактически никогда не захотите выполнять фоновую операцию как показано здесь.

Но этот подход может чрезвычайно хорошо работать, если Вы можете помещать фоновую операцию непосредственно в функцию BackgroundFuncApt! Например: Вы могли бы иметь фоновый поток, выполняющий фоновые вычисления или операцию ожидания системы. Когда они будут завершены, вы можете вызывать метод объекта, который сгенерирует событие в клиенте. Храня количество вызовов метода, небольшое относительно количества работы, выполняемой в фоновой функции, Вы можете достигать очень эффективных результатов.

Что, если Вы хотите выполнить фоновую операцию, которая не должна использовать объект? Очевидно, проблемы с соглашением COM о потоках исчезают. Но появляются другие проблемы. Как фоновый поток сообщит о своем завершении приоритетному потоку? Как они обмениваются данными? Как два потока будут синхронизированы? Все это возможно выполнить с помощью соответствующих вызовов API. В моей книге Visual Basic 5.0 Programmer's Guide to the Win32 API имеется информации относительно объектов синхронизации типа Событий, Mutexes, Семафоров и Waitable Таймеров.

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

Заключение

Я однажды услышал от опытного программиста под Windows, что OLE является самой трудной технологией, которой он когда-либо обучался. Я с этим согласен. Это очень обширная тема, и некоторые части этой технологии очень трудно понять. Visual Basic, как всегда, скрывает от Вас много сложностей.

Имеется сильное искушение, чтобы пользоваться преимуществом продвинутых методов типа многопоточного режима, используя подход "tips and techniques". Это искушение поощрено некоторыми статьями, которые иногда представляют специфическое решение, приглашая Вас вырезать и вставить (cut and past) их методики в ваши собственные приложения.

Когда я писал книгу Visual Basic Programmer's Guide to the Windows API, я выступал против такого подхода к программированию. Я чувствовал, что вообще безответственно включать в приложение код, который Вы не понимаете, и что реальное знание, которое так тяжело получить, стоит затраченных усилий.

Таким образом мои книги по API были разработаны, чтобы обеспечить не быстрые ответы и простые решения, а чтобы обучить использованию API к такой степени, что программисты могли бы интеллектуально правильно применять даже наиболее продвинутые методы. Я применил это тот же самый подход к моей книге Developing ActiveX Components, которая требует много времени для обсуждения принципов ActiveX, COM и объектно-ориентированного программирования перед описанием подробностей реализации этой технологии.

Многое из моей карьеры на ниве Visual Basic и многое из деятельности в фирме Desaware, основано на обучении Visual Basic программистов продвинутым методам. Читатель, кто вдохновил меня на написание этой статьи, критикуя меня за сдерживание технологии многопоточности, пропустил точку.

Да, я обучаю и демонстрирую продвинутые методы программирования - но я пытаюсь никогда не пропустить большую картинку. Продвинутые методы, которым я обучаю, должны быть непротиворечивы с правилами и спецификациями Windows. Они должны быть такими безопасными, насколько это возможно. Они должны быть поддерживаемыми в конечном счете. Они не должны разрушаться, когда изменяются Windows или Visual Basic.

Я могу требовать только частичного успеха, так как Microsoft может изменить правила игры всякий раз, когда им покажется, что это необходимо. Но я всегда помню об этом и пробую предупреждать людей, когда я думаю, что могу протолкнуть ограничение.

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

Я не могу обещать, что использование apartment model версии CreateThread является абсолютно корректным, но мое понимание проблемы и опыт показывают, что это безопасно.

Могут иметься другие факторы, которые я пропустил. OLE - действительно сложная вещь и модули OLE DLL и сам Visual Basic подвержены изменениям. Я только могу утверждать, что лучшее из моего знания - код, который я здесь показал, удовлетворяет правилам COM и что эмпирическое доказательство показывает, что Visual Basic runtime 5 0's является достаточно безопасным для выполнения фонового кода потока в стандартном модуле.

Список литературы

Для подготовки данной работы были использованы материалы с сайта https://matfak.ru/



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2020-04-01 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: