Главная страница
Навигация по странице:

  • DoTheCount

  • MessageBox

  • CreateThread

  • CloseHandle

  • BackgroundFuncFree

  • ByVal param As IUnknown

  • MTDEMO3

  • StartBackgroundThreadApt

  • IDispatch

  • CoMarshalInterThreadInterfaceInStream

  • Леонтьев Б.К. Я изучаю Microsoft Office Visio 2003 (PDF). Удк 004. 738. 5 Ббк 32. 973. 26 018. 2


    Скачать 0.88 Mb.
    НазваниеУдк 004. 738. 5 Ббк 32. 973. 26 018. 2
    АнкорЛеонтьев Б.К. Я изучаю Microsoft Office Visio 2003 (PDF).pdf
    Дата25.04.2017
    Размер0.88 Mb.
    Формат файлаpdf
    Имя файлаЛеонтьев Б.К. Я изучаю Microsoft Office Visio 2003 (PDF).pdf
    ТипДокументы
    #4822
    КатегорияИнформатика. Вычислительная техника
    страница32 из 35
    1   ...   27   28   29   30   31   32   33   34   35
    COM поддерживает потоки?
    Если вы разбираетесь в COM, то знаете, что он определяет струк туру соглашения. Объект COM соглашается следовать некоторым прави лам так, чтобы этим можно было успешно пользоваться из любого при ложения или объекта, который поддерживает COM.
    Большинство людей сначала думает о интерфейсной части согла шения — о методах и свойствах, которые предоставляет объект.
    Но вы не можете не знать того, что COM также определяет поточ ность как часть соглашения. И подобно любой части соглашения COM
    — если вы нарушаете эти условия, то возникнут проблемы.
    Visual Basic, естественно, скрывает от вас большинство механиз мов COM, но чтобы понять как использовать многопоточность в Visual
    Basic, вы должны разобраться в COM модели потоков.
    Модель одиночного потока
    Однопоточный сервер — самый простой тип реализации сервера.
    И самый простой для понимания. В этом случае
    EXE сервер выполняет ся в одиночном потоке. Все объекты создаются в этом потоке.
    Все вызовы методов каждого объекта, поддерживаемого сервером должны прибыть в этот поток.
    Но что будет, если клиент задействован в другом потоке? В таком случае для объекта сервера должен быть создан промежуточный объект
    (
    proxy object). Этот промежуточный объект выполняется в потоке клиен та и отражает методы и свойства фактического объекта. Когда вызывает ся метод промежуточного объекта, он выполняет операции, необходи мые для подключения к потоку объекта, а затем вызывает метод фактического объекта, используя параметры, переданные к промежуточ ному объекту. Естественно, что этот подход требует значительного вре мени на выполнение задачи, однако он позволяет выполнить все согла шения. Этот процесс переключения потоков и пересылки данных от промежуточного объекта к фактическому объекту и обратно называется
    marshalling.
    В случае DLL серверов, одиночная потоковая модель требует, что бы все объекты в сервере создавались и вызывались в том же самом пото ке что и первый объект, созданный сервером.
    Модель Apartment Threading
    Модель
    Apartment Threading как определено COM не требует, что бы каждый поток имел собственный набор глобальных переменных.
    Какую модель поддерживает ваш сервер?
    Как приложение или ОС Windows узнает, которую модель потоков использует сервер? Эта информация включена в системный реестр (
    reg
    istry). Когда Visual Basic создает объект, он проверяет системный реестр,
    чтобы определить, в каких случаях требуется использовать промежуточ ный объект (
    proxy object) и в каких — marshalling.
    Эта проверка является обязанностью клиента и необходима для строгой поддержки требований многопоточности для каждого объекта,
    который он создает.
    Функция API CreateThread
    Теперь давайте посмотрим, как с Visual Basic может использовать ся функция API
    CreateThread.
    Скажем, есть в наличии класс, который вы хотите выполнять в другом потоке, к примеру, чтобы выполнить некоторую фоновую опера
    Использование редактора Visual Basic
    337 338
    Использование редактора Visual Basic
    цию. Характерный класс такого типа мог бы иметь следующий код (из примера
    MTDemo 3):
    ' Class clsBackground
    ' MTDemo 3 — Multithreading example
    ' Copyright © 2003 by Desaware Inc. All Rights Reserved
    Option Explicit
    Event DoneCounting()
    Dim l As Long
    Public Function DoTheCount(ByVal finalval&) As Boolean
    Dim s As String
    If l = 0 Then s$ = "In Thread " & App.threadid
    Call MessageBox(0, s$, "", 0)
    End If l = l + 1
    If l >= finalval Then l = 0
    DoTheCount = True
    Call MessageBox(0, "Done with counting", "", 0)
    RaiseEvent DoneCounting
    End If
    End Function
    Класс разработан так, чтобы функция
    DoTheCount могла неодно кратно вызываться из непрерывного цикла в фоновом потоке. Мы могли бы поместить цикл непосредственно в сам объект, но вы вскоре увидите,
    что были веские причины для проектирования объекта, как показано в примере.
    При первом вызове функции
    DoTheCount появляется MessageBox,
    в котором показан идентификатор потока, по которому мы можем опре делить поток, в котором выполняется код. Вместо Visual Basic 6.3 коман ды
    MessageBox используется MessageBox API, потому что функция API,
    как известно, поддерживает безопасное выполнение потоков. Второй
    MessageBox появляется после того, как закончен подсчет и сгенерирова но событие, которое указывает, что операция закончена.
    Фоновый поток запускается при помощи следующего кода в фор ме
    frmMTDemo3:
    Private Sub cmdCreateFree_Click()
    Set c = New clsBackground
    StartBackgroundThreadFree c
    End Sub
    Функция
    StartBackgroundThreadFree определена в модуле modMT
    Back следующим образом:
    Declare Function CreateThread Lib "kernel32" (ByVal lpSecurityAttributes _
    As Long, ByVal dwStackSize As Long, ByVal lpStartAddress As Long,
    _
    ByVal lpParameter As Long, ByVal dwCreationFlags As Long, _
    lpThreadId As Long) As Long
    Declare Function CloseHandle Lib «kernel32» (ByVal hObject As
    Long) As Long
    ' Start the background thread for this object
    ' using the invalid free threading approach.
    Public Function StartBackgroundThreadFree(ByVal qobj As clsBackground)
    Dim threadid As Long
    Dim hnd&
    Dim threadparam As Long
    ' Free threaded approach threadparam = ObjPtr(qobj)
    hnd = CreateThread(0, 2003, AddressOf BackgroundFuncFree, _
    threadparam, 0, threadid)
    If hnd = 0 Then
    ' Return with zero (error)
    Exit Function
    End If
    ' We don't need the thread handle
    CloseHandle hnd
    StartBackgroundThreadFree = threadid
    End Function
    Функция
    CreateThread имеет шесть параметров:

    lpSecurityAttributes — обычно устанавливается в нуль,
    чтобы использовать заданные по умолчанию атрибуты защиты.

    dwStackSize — размер стека. У каждого потока есть собственный стек.

    lpStartAddress — адрес памяти, где стартует поток. Он должен быть равен адресу функции в стандартном модуле,
    полученном при использовании оператора
    AddressOf.

    lpParameter — long 32 разрядный параметр, который передается функции, запускающей новый поток.

    dwCreationFlags — 32 бит переменная флагов, которая позволяет вам управлять запуском потока (активный,
    приостановленный и т. д.).
    Использование редактора Visual Basic
    339 340
    Использование редактора Visual Basic


    lpThreadId — переменная, в которую загружается уникальный идентификатором нового потока.
    Функция возвращает дескриптор потока.
    В этом случае мы передаем указатель на объект
    clsBackground, ко торый мы будем использовать в новом потоке.
    ObjPtr восстанавливает значение указателя интерфейса в переменную
    qobj. После создания по тока закрывается дескриптор при помощи функции
    CloseHandle. Это действие не завершает поток, — поток продолжает выполняться до вы хода из функции
    BackgroundFuncFree. Однако если мы не закрыли дес криптор, то объект потока будет существовать даже после выхода из функции
    BackgroundFuncFree. Все дескрипторы потока должны быть за крыты и при завершении потока система освобождает занятые потоком ресурсы.
    Функция
    BackgroundFuncFree имеет следующий код:
    ' A free threaded callback.
    ' A free threaded callback.
    ' This is an invalid approach, though it works
    ' in this case.
    Public Function BackgroundFuncFree(ByVal param As IUnknown) As
    Long
    Dim qobj As clsBackground
    Dim res&
    ' Free threaded approach
    Set qobj = param
    Do While Not qobj.DoTheCount(100000)
    Loop
    ' qobj.ShowAForm ' Crashes!
    ' Thread ends on return
    End Function
    Параметром этой функции является указатель на интерфейс
    (
    ByVal param As IUnknown). При этом мы можем избежать неприятнос тей, потому что под COM каждый интерфейс основывается на
    IUnknown,
    так что такой тип параметра допустим независимо от типа интерфейса,
    передаваемого функции. Мы, однако, должны немедленно определить param как тип объекта, чтобы затем его использовать. В этом случае
    qobj
    устанавливается как объект
    clsBackground, который был передан к объек ту
    StartBackgroundThreadFree.
    Функция затем выполняет бесконечный цикл, в течение которого может выполняться любая требуемая операция, в этом случае использу ется повторный счет. Подобный подход мог бы использоваться здесь,
    чтобы выполнить операцию ожидания, которая приостанавливает поток пока не произойдет системное событие (типа завершения процесса). По ток затем мог бы вызвать метод класса, чтобы сообщить приложению,
    что событие произошло.
    Доступ к объекту
    qobj чрезвычайно быстр из за использования подхода свободного потока (
    free threading) — никакая переадресация
    (
    marshalling) при этом не используется.
    Обратите внимание на то, что если вы попробуете использовать объект
    clsBackground, который показывает форму, то это приведет к сбо ям приложения. Обратите также внимание на то, что событие заверше ния никогда не происходит в клиентской форме.
    Действительно, даже Microsoft Systems Journal, который описы вает этот подход, содержит очень много предупреждений о том, что при использовании этого подхода есть некоторые вещи, которые не работают.
    Является ли это дефектом в Visual Basic?
    Некоторые разработчики, кто пробовали развертывать приложе ния, применяющие этот тип многопоточности, обнаружили, что их при ложения вызывают сбои после обновления.
    Означает ли эта проблема, что Microsoft не сумел правильно обеспечить обратную совместимость?
    Ответ на оба вопроса: Нет Проблема не в Microsoft или Visual
    Basic.
    Вышеупомянутый код является мусором. Проблема проста —
    Visual Basic поддерживает объекты и в модели одиночного потока и в
    apartment model.
    Что это означает?
    Поведение объекта подчиненно изменениям, так как Visual Basic постоянно модифицируется.
    Это означает, что любая попытка объекта обратиться к другим объектам или формам может потерпеть неудачу и что причины отказов могут изменяться, поскольку эти объекты модифицируются.
    Даже код, который сейчас работает, может внезапно вызвать сбой,
    поскольку другие объекты добавляются, удаляются или изменяются.
    Это означает, что невозможно характеризовать поведение прило жения или предсказать, будет ли оно работать или может ли работать в любой данной среде.
    Невозможно предсказать, будет ли код работать на любой данной системе, и что его поведение может зависеть от используемой операци
    Использование редактора Visual Basic
    341 342
    Использование редактора Visual Basic
    онной, числа используемых процессоров и других проблем конфигура ции системы.
    Вы видите, что как только вы нарушаете соглашение COM, вы больше не защищены теми возможностями COM, которые позволяют объектам успешно взаимодействовать друг с другом и с клиентами.
    Этот подход является программной алхимией. Это безответствен но и ни один программист не должен поступать таким образом.
    Обратно к функции API CreateThread
    Теперь, когда стало понятно, почему подход к использованию
    CreateThread API является мусором, покажем, как можно использовать эту API функцию безопасно.
    Прием прост — вы должны просто твердо придерживаться согла шения COM о потоках. Это займет немного больше времени и усилий,
    но практика показала, что получаются очень надежные результаты.
    Пример
    MTDEMO3 демонстрирует этот подход в форме frm
    MTDemo3, имеющей код, который запускает класс фона в apartment
    model следующим образом:
    Private Sub cmdCreateApt_Click()
    Set c = New clsBackground
    StartBackgroundThreadApt c
    End Sub
    Пока этот процесс очень похож на подход свободных потоков. Вы создаете экземпляр класса и передаете его функции, которая запускает фоновый поток. В модуле
    modMTBack появляется следующий код:
    ' Structure to hold IDispatch GUID
    Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
    End Type
    Public IID_IDispatch As GUID
    Declare Function CoMarshalInterThreadInterfaceInStream Lib
    "ole32.dll" _
    (riid As GUID, ByVal pUnk As IUnknown, ppStm As Long) As Long
    Declare Function CoGetInterfaceAndReleaseStream Lib "ole32.dll" _
    (ByVal pStm As Long, riid As GUID, pUnk As IUnknown) As Long
    Declare Function CoInitialize Lib "ole32.dll" (ByVal pvReserved
    As Long) As Long
    Declare Sub CoUninitialize Lib "ole32.dll" ()
    ' Start the background thread for this object
    ' using the apartment model
    ' Returns zero on error
    Public Function StartBackgroundThreadApt(ByVal qobj As clsBackground)
    Dim threadid As Long
    Dim hnd&, res&
    Dim threadparam As Long
    Dim tobj As Object
    Set tobj = qobj
    ' Proper marshaled approach
    InitializeIID
    res = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, qobj,
    threadparam)
    If res <> 0 Then
    StartBackgroundThreadApt = 0
    Exit Function
    End If hnd = CreateThread(0, 2003, AddressOf BackgroundFuncApt, thread param, 0, threadid)
    If hnd = 0 Then
    ' Return with zero (error)
    Exit Function
    End If
    ' We don't need the thread handle
    CloseHandle hnd
    StartBackgroundThreadApt = threadid
    End Function
    Функция
    StartBackgroundThreadApt немного более сложна, чем ее эквивалент при применении подхода свободных потоков. Первая новая функция называется
    InitializeIID. Ее код таков:
    ' Initialize the GUID structure
    Private Sub InitializeIID()
    Static Initialized As Boolean
    If Initialized Then Exit Sub
    With IID_IDispatch
    .Data1 = &H20400
    .Data2 = 0
    .Data3 = 0
    .Data4(0) = &HC0
    .Data4(7) = &H46
    End With
    Использование редактора Visual Basic
    343 344
    Использование редактора Visual Basic

    Initialized = True
    End Sub
    Вы видите, нам необходим идентификатор интерфейса — 16 бай товая структура, которая уникально определяет интерфейс. В частности,
    нам нужен идентификатор для интерфейса
    IDispatch. Функция
    InitializeIID инициализирует структуру IID_IDISPATCH к корректным значениям для идентификатора интерфейса
    IDispatch. Это значение по лучается с помощью использования утилиты просмотра системного рее стра.
    Зачем нам этот идентификатор? Чтобы твердо придерживаться со глашения 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) — относительно медленные операции. Вы фактически никогда не захотите выполнять фоновую операцию как по казано здесь.
    Но этот подход может чрезвычайно хорошо работать, если вы мо жете помещать фоновую операцию непосредственно в функцию
    1   ...   27   28   29   30   31   32   33   34   35


    написать администратору сайта