ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Потоки в Visual Basic
         

     

    Інформатика, програмування

    Потоки в Visual Basic

    З появою оператора AddressOf, частина індустрії ПО стала орієнтуватися на авторів, що показують як з використанням Visual Basic вирішувати раніше неможливі завдання. Інша частина швидко охопила консультантів, які допомагають користувачам, що мають проблеми при вирішенні таких завдань.

    Проблема не в Visual Basic або в технології. Проблема в тому, що більшість авторів застосовують одне й те ж правило до AddressOf методиками, що більшість компаній з розробки ПЗ вважають, що якщо Ви повинні щось зробити, то Ви зможете. Ідея про те, що застосування самої нової і останньої технології повинно, за визначенням, бути найкращим вирішенням проблеми, широко поширена в індустрії ПЗ.

    Ця ідея невірна. Розгортання технології повинно керуватися перш за все проблемою, яку необхідно вирішити вирішити, а не технологією, яку хтось пробує Вам впарити;).

    Дуже погано, що з-за того, що компанії часто нехтують згадуванням про обмеження та недоліки їх інструментальних засобів, автори інколи бувають не в змозі звернути увагу читачів на наслідки деяких методик, які вони описують. І журнали і книги іноді нехтують своєю відповідальністю, щоб упевнитися, що практика програмування, яку вони описують, є прийнятною.

    Програмістові дуже важливо вибрати необхідний інструмент для своєї роботи. Це - ваше завдання, щоб розробити код, який працює тепер не тільки на одній специфічній платформі, але також працює на різних платформах і системних конфігураціях. Ваш код повинен бути добре документована і підтриманий іншими програмістами, які беруть участь у проекті. Ваш код повинен слідувати правилам, продиктованими операційною системою або стандартами, які Ви використовуєте. Відмова так робити може призвести до проблем у майбутньому, оскільки системи і програмне забезпечення постійно удосконалюються.

    Недавні статті в Microsoft Systems Journal і Visual Basic Programmer's Journal представили програмістам на Visual Basic можливість використання функції API CreateThread, щоб безпосередньо підтримувати багато-режим під Visual Basic. Після цього, один читач поскаржився, що моя книга Visual Basic Programmer's Guide to the Win32 API є неповною, тому що я не описав в ній цю функцію і не продемонстрував цю технологію. Ця стаття -- частково є відповіддю цього читачеві, і частково - відповіддю на інші статті, написаними на цю тему. Ця стаття також є доповненням до глави 14 моєї книги "Розробка ActiveX компонент на Visual Basic 5.0 "щодо нових можливостей, що забезпечуються Visual Basic 5.0 Service Pack 2.

    Швидкий огляд багатопоточності

    Якщо Ви вже добре розбираєтеся в технології багатопотокового режиму, то Ви можете пропустити цей розділ і продовжувати читання з розділу, названого "Що нового в Service Pack 2."

    Кожен, хто використовує Windows, знає, що Windows здатне робити більше ніж одну річ одночасно.

    Може одночасно виконувати декілька програм, при одночасному програванні компакт-диска, посилці факсу і пересилання файлів. Кожен програміст знає (або повинен знати) що ЦЕНТРАЛЬНИЙ ПРОЦЕСОР комп'ютера може тільки виконувати одну команду одночасно (проігноруємо існування багатопроцесорних машин). Як єдиний ЦЕНТРАЛЬНИЙ ПРОЦЕСОР може виконувати безліч завдань?

    Це робиться швидким перемиканням між багатьма завданнями. Операційна система містить в пам'яті всі програми, які запущені в даний момент. Це дозволяє Центральний процесор виконувати програми по черзі. Кожного разу відбувається перемикання між програмами, при цьому змінюється вміст внутрішніх регістрів, включаючи покажчик команди і покажчик вершини стека. Кожна з таких "завдань" називається потоком виконання (thread of execution).

    В простий багатозадачного системі, кожна програма має емеет єдиний потік. Це означає, що ЦЕНТРАЛЬНИЙ ПРОЦЕСОР починає виконання команд на початку програми і продовжує дотримуючись інструкцій в послідовності, визначеній програмою до тих пір, поки програма не завершується.

    Скажімо, програма має п'ять команд: BCD і E, що виконуються послідовно (ніяких переходів немає в цьому прикладі). Коли програма має один потік, команди будуть завжди виконувати в точно тому ж самому порядку: A, B, C, D і E. Дійсно, ЦЕНТРАЛЬНИЙ ПРОЦЕСОР може зажадати часу для виконання інших команд в інші програмах, але вони не будуть впливати на цей додаток, якщо не є конфлікт над загальними ресурсами системи, але це вже окрема тема для розмови.

    Просунута багатопотокова операційна система типу Windows дозволяє додатку виконувати більше ніж один потік одночасно. Скажімо, команда D в нашому типовому додатку могла створити новий потік, який стартував командою B і далі виконував послідовність команд C і E. Перший потік був би все ще A, B, C, D, E, та коли команда D виконається, виникне новий потік, який виконає команди б B, C, E (тут команди D вже не буде, інакше ми отримаємо ще один потік).

    В якому порядку будуть слідувати команди в цьому додатку?

    Це могло б бути:

    Thread 1 A B C D E E

    Thread 2 B C

    Або так:

    Thread 1 A B C D E

    Thread 2 B C E

    Або отак:

    Thread 1 A B C D E

    Thread 2 B C E

    Іншими словами, коли Ви починаєте новий потік виконання в програмі, Ви ніколи не можете знати точний порядок, в якому команди в двох потоках виконуватися щодо один одного. Два потоки повністю незалежні.

    Чому - Це проблема?

    Імітатор багатопоточності

    Розглянемо проект MTDemo:

    Проект містить один модуль коду, в якому міститься два глобальних змінних:

    'MTDemo - Multithreading Demo program

    'Copyright © 1997 by Desaware Inc. All Rights Reserved

    Option Explicit

    Public GenericGlobalCounter As Long

    Public TotalIncrements As Long

    ' Цей проект містить одну форму - frmMTDemo1, яка містить

    'наступний код:

    'MTDemo - Multithreading Demo program

    'Copyright © 1997 by Desaware Inc. All Rights Reserved

    Option Explicit

    Dim State As Integer

    'State = 0 - Idle

    'State = 1 - Loading existing value

    'State = 2 - Adding 1 to existing value

    'State = 3 - Storing existing value

    'State = 4 - Extra delay

    Dim Accumulator As Long

    Const OtherCodeDelay = 10

    Private Sub Command1_Click ()

    Dim f As New frmMTDemo1

    f.Show

    End Sub

    Private Sub Form_Load ()

    Timer1.Interval = 750 + Rnd * 500

    End Sub

    Private Sub Timer1_Timer ()

    Static otherdelay &

    Select Case State

    Case 0

    lblOperation = "Idle"

    State = 1

    Case 1

    lblOperation = "Loading Acc "

    Accumulator = GenericGlobalCounter

    State = 2

    Case 2

    lblOperation = "Incrementing"

    Accumulator = Accumulator + 1

    State = 3

    Case 3

    lblOperation = "Storing"

    GenericGlobalCounter = Accumulator

    TotalIncrements = TotalIncrements + 1

    State = 4

    Case 4

    lblOperation = "Generic Code "

    If otherdelay> = OtherCodeDelay Then

    State = 0

    otherdelay = 0

    Else

    otherdelay = otherdelay + 1

    End If

    End Select

    UpdateDisplay

    End Sub

    Public Sub UpdateDisplay ()

    lblGlobalCounter = Str $ (GenericGlobalCounter)

    lblAccumulator = Str $ (Accumulator)

    lblVerification = Str $ (TotalIncrements)

    End Sub

    Ця програма для моделювання багатопотокового режиму використовує таймер і простий кінцевий автомат. Змінна State описує п'ять команд, які ця програма виконує. State = 0 - пасивний стан. State = 1 завантажує локальну змінну глобальної змінної GenericGlobalCounter. State = 2 збільшує на одиницю локальну змінну. State = 3 запам'ятовує результат у змінній GenericGlobalCounter і збільшує змінну TotalIncrements (яка вважає кількість збільшень змінної GenericGlobalCounter). State = 3 додає додаткову затримку, що представляє собою час, витрачений на виконання інших команд в програмі.

    Функція UpdateDisplay оновлює три мітки на формі, які показують поточне значення змінної GenericGlobalCounter, локального суматора, і загальної кількості збільшень.

    Кожен сигнал таймера моделює цикл центральний процесор у поточному потоці. Якщо Ви запустіть програму, то побачите, що значення змінної GenericGlobalCounter буде завжди точно так само змінної TotalIncrements, тому що мінлива TotalIncrements показує кількість збільшень лічильника GenericGlobalCounter потоком.

    Але що станеться, коли Ви натискаєте кнопку Command1 і запустіть другий примірник форми? Ця нова форма змоделює другий потік.

    Час від часу, команди вишикуються в лінію таким чином, що обидві форми завантажать однакове значення GenericGlobalCounter, збільшать і збережуть його. У результаті, значення лічильника збільшиться тільки на одиницю, навіть при те, що кожен потік думав, що він незалежно збільшує значення лічильника.

    Іншими словами, мінлива була збільшена двічі, але значення збільшилося тільки на одиницю. Якщо ви запускаєте кілька форм, то відразу помітите, що число збільшень, що представляється змінною TotalIncrements, зростає набагато швидше, ніж лічильник GenericGlobalCounter.

    Що, якщо змінна представляє Об'єктовий рахунок блокування - який стежить, коли об'єкт повинен бути звільнений? Що, якщо вона являє собою сигнал, який вказує, що ресурс знаходиться у використанні?

    Така проблема може привести до появи ресурсів, постійно недоступних в системі, до об'єкта, який блокує в пам'яті, або передчасно звільнено. Це може призвести до збоїв програми.

    Цей приклад був розроблений, щоб достатньо просто побачити проблему, але спробуйте поекспериментувати зі значенням змінної OtherCodeDelay. Коли небезпечний код відносно невеликий в порівнянні з усією програмою, проблеми з'являться менш часто. Хоча це і звучить обнадійливо, але істина полягає в наступному. Проблеми Багатопотокового режиму можуть бути надзвичайно нестійкі і їх важко виявити. Це означає, що багато-режим потребує обережного підходу до проектування програми.

    Вирішення проблем багатопоточності

    Є два відносно простих способи уникнути проблем багатопотокового режиму.

    Уникайте загального використання глобальних змінних.

    Додайте код синхронізації скрізь, де використовуються глобальні змінні.

    Перший підхід використовується в основному в Visual Basic. Коли Ви вмикаєте багато - режим в Visual

    Basic додатки, всі глобальні змінні стануть локальними для специфічного потоку. Це властиво способу, з яким Visual Basic виконує apartment model threading - докладніше про це пізніше.

    Початковий випуск Visual Basic 5.0 дозволяв використовувати багатопоточність тільки для компонентів, які не мали ніяких елементів призначеного для користувача інтерфейсу. Так було тому що вони не мали безпечного потоку управління формами. Наприклад: коли Ви створюєте форму в Visual Basic, VB дає їй ім'я глобальної змінної (таким чином, якщо Ви маєте форму, іменовану Form1, Ви можете безпосередньо звертатися до її методів, використовуючи Form1.метод замість того, щоб оголосити окрему змінну форми). Цей тип глобальної змінної може викликати проблеми багатопотокового режиму, які Ви бачили раніше. Були безсумнівно інші проблеми всередині управління формами.

    З service pack 2, керування формами Visual Basic було зроблено безпечним потоком. Це говорить про те, що кожен потік має власну глобальну змінну для кожної форми, визначеної в проекті.

    Що нового в Service Pack 2

    Зробивши потік управління формами безпечним, Service pack 2 надав можливість з допомогою Visual

    Basic створювати клієнтські додатки, що використовують багато-режим.

    Додаток повинно бути визначено як програма ActiveX Exe з установкою запуску з Sub Main:

    'MTDemo2 - Multithreading demo program

    'Copyright © 1997 by Desaware Inc. All Rights Reserved

    Option Explicit

    Declare Function FindWindow Lib "user32" Alias "FindWindowA" _

    (ByVal lpClassName As String, ByVal lpWindowName As String) _

    As Long

    Sub Main ()

    Dim f As frmMTDemo2

    'We need this because Main is called on each new thread

    Dim hwnd As Long

    hwnd = FindWindow (vbNullString, "Multithreading Demo2")

    If hwnd = 0 Then

    Set f = New frmMTDemo2

    f.Show

    Set f = Nothing

    End If

    End Sub

    Перший разу програма завантажує і відображає основну форму додатку. Підпрограма Main має з'ясувати, чи є це першим потоком програми, тому цей код виконується при старті кожного потоку. Ви не можете використовувати глобальну змінну, щоб це з'ясувати, бо Visual Basic apartment model зберігає глобальні змінні специфічними для одиночного потоку. У цьому прикладі використовується функція API FindWindow, щоб перевірити, чи була завантажена основна форма прикладу.

    Є інші способи з'ясувати, чи є це основним потоком, включаючи використання об'єктів синхронізації системи - але це окрема тема для розмови.

    Багатопотокові режим реалізується створенням об'єкта в новому потоці. Об'єкт повинен бути визначений, використовуючи модуль класу. У цьому випадку, простий модуль класу визначається наступним чином:

    'MTDemo2 - Multithreading demo program

    'Copyright © 1997 by Desaware Inc. All Rights Reserved

    Option Explicit

    Private Sub Class_Initialize ()

    Dim f As New frmMTDemo2

    f.Show

    Set f = Nothing

    End Sub

    Ми можемо встановити змінну форми як nothing після того, як вона створена, тому що після відображення форми вона буде збережена.

    'MTDemo2 - Multithreading demo program

    'Copyright © 1997 by Desaware Inc. All Rights Reserved

    Option Explicit

    Private Sub cmdLaunch1_Click ()

    Dim c As New clsMTDemo2

    c.DisplayObjPtr Nothing

    End Sub

    Private Sub cmdLaunch2_Click ()

    Dim c As clsMTDemo2

    Set c = CreateObject ( "MTDemo2.clsMTDemo2")

    End Sub

    Private Sub Form_Load ()

    lblThread.Caption = Str $ (App.ThreadID)

    End Sub

    Форма відображає ідентифікатор потоку в мітці на формі. Форма містить дві командні кнопки, один з яких використовує оператор New, інша-використовує оператор CreateObject.

    Якщо Ви запустіть програму всередині середовища Visual Basic, то побачите, що форми завжди створюються в одному і тому ж потоці. Це відбувається, тому що середовище Visual Basic підтримує тільки одиночний потік. Якщо Ви скомпіліруете і запустіть програму, то побачите, що підхід, який використовує CreateObject створює і clsMTDemo2 та її форму у новому потоці.

    Чому багатопоточність

    Звідки вся метушня щодо багатопотокового режиму, якщо він включає так багато потенційної небезпеки? Тому що, в деяких ситуаціях, багато-режим може значно покращувати ефективність програми. У деяких випадках це може покращувати ефективність деяких операцій синхронізації типу очікування завершення додатки. Це дозволяє зробити архітектуру програми більш гнучкою. Наприклад, операція Add a long у формі MTDEMO2 з наступним кодом:

    Private Sub cmdLongOp_Click ()

    Dim l &

    Dim s $

    For l = 1 To 1000000

    s = Chr $ (l And & H7F)

    Next l

    End Sub

    Запустіть кілька екземплярів форми, використовуючи кнопку cmdLaunch1. Коли Ви натискаєте на кнопку cmdLongOp на будь-який з форм, то побачите, що ця дія заморожує операції на всіх інших формах. Так відбувається, тому що всі форми виконуються в одиночному потоці - і цей потік зайнятий виконанням довгого циклу. Якщо Ви запустіть кілька екземплярів форми кнопкою cmdLaunch2 і нажімете кнопку cmdLongOp на форму, то тільки ця форма буде заморожена - інші форми будуть активними. Вони виконуються у власних потоках, і довгий цикл буде виконуватися тільки у власному потоці.

    Звичайно, в будь-якому випадку, Ви, ймовірно, не повинні розміщувати тривалі операції такого типу у ваших формах.

    Далі коротке резюме, коли важливий багато-режим:

    Сервер ActiveX EXE - без загальних ресурсів.

    Коли Ви маєте ActiveX EXE сервер, який Ви збираєтеся спільно використовувати серед декількох додатків, багато-режим запобігає програми від небажаних взаємодій з один одним. Якщо одне додаток виконує довгу операцію на об'єкті в однопотоковим сервер, інші програми будуть витіснені, тобто будуть чекати, коли звільниться сервер. Багато-режим рещает цю проблему. Проте, є випадки, де Ви можете хотіти використовувати ActiveX EXE сервер, щоб регулювати доступ до загальнодоступних ресурсах (shared resource). Наприклад, сервер stock quote, описаний у моїй книзі Developing ActiveX Components. У цьому випадку сервер stock quote виконується в одиночному потоці і що доступний для всіх додатків, що використовують сервер з черги.

    багато-клієнт - виконуваний як ActiveX EXE сервер

    Проста форма цього підходу продемонстрована в додатку MTDEMO2. Цей підхід використовується, коли програма підтримує множинні вікна, які повинні виходити з однієї програми, але працювати повністю незалежно. Інтернет-браузер - гарний приклад такого багатопотокового клієнта, де кожне вікно виконується у власному потоці. Тут слід звернути увагу на те, що багато - режим не повинен використовуватися як заміна для гарного подієво керованого проекту.

    Багатопотокові сервери DLL або EXE

    В архітектурі клієнт-сервер, багато-режим може збільшити ефективність, якщо Ви маєте суміш довгих і коротких клієнтських запитів. Будьте уважним, хоча - якщо всі ваші клієнтські запити мають подібну довжину, багато-режим може фактично сповільнювати середній час відповіді сервера! Ніколи не приймайте на віру той факт, що якщо ваш сервер є багатопоточність, то обов'язково його ефективність збільшиться.

    Угода про потоках

    вірите чи ні, але все це було введенням. Частина цього матеріалу є оглядом матеріалу, який описаний у моїй книзі Developing ActiveX Components, інша частина матеріалу описує нову інформацію для service pack 2.

    Тепер, дозволите задавати питання, яке має відношення до багатопотоковому режиму, що використовує COM (модель багатокомпонентних об'єктів, на якій засновані не тільки всі Visual Basic об'єкти, а й інші windows програми, використовують технології OLE).

    Дано:

    Багатопотокові режим є потенційно небезпечним взагалі, і особливо спроби багатопотокового кодування додатків, які не розроблені для підтримки багатопотокового режиму, швидше за все призведуть до фатальних помилок і збоїв системи.

    Запитання:

    Як це можливо, що Visual Basic дозволяє Вам створювати об'єкти і використовувати їх з поодинокими і багато-середовищами безвідносно до того, розроблені вони для одиночного або багатопотокового використання?

    Іншими словами - Як багатопотокові Visual Basic додатки можуть використовувати об'єкти, які не розроблені для безпечного виконання в многопоточной середовищі? Як можуть інші багатопотокові програми використовувати Однопотокові об'єкти Visual Basic?

    Коротко: як COM підтримує потоки?

    Якщо Ви знаєте COM, то Ви знаєте, що COM визначає структуру угоди. Об'єкт COM погоджується слідувати деяким правилами так, щоб цим можна було успішно користуватися з будь-якої програми або об'єкта, що підтримує COM.

    Більшість людей спочатку думає про інтерфейсній частини угоди - про методи і властивості, які надає об'єкт.

    Але Ви не можете не знати того, що COM також визначає потоковість як частина угоди. І подібно до будь-якої частини угоди COM - якщо Ви порушуєте ці умови, то будете мати проблеми. Visual Basic, природно, приховує від Вас більшість механізмів COM, але щоб зрозуміти як використовувати багатопоточність в Visual Basic, Ви повинні розібратися COM моделі потоків.

    Модель одиночного потоку:

    Однопотокові сервер - найпростіший тип реалізації сервера. І найпростіший для розуміння.

    В цьому випадку EXE сервер виконується в одиночному потоці. Всі об'єкти створюються в цьому потоці.

    Всі виклики методів кожного об'єкта, що підтримується сервером повинні прибути в цей потік.

    Але що буде, якщо клієнт виконується в іншому потоці? У тому випадку, для об'єкта сервера повинен бути створений проміжний об'єкт (proxy object). Цей проміжний об'єкт виконується в потоці клієнта і відображає методи і властивості фактичного об'єкта. Коли викликається метод проміжного об'єкта, він виконує операції, необхідні для підключення до потоку об'єкта, а потім викликає метод фактичного об'єкта, використовуючи параметри, передані до проміжного об'єкту. Природно, що цей підхід вимагає значного часу на виконання завдання, проте він дозволяє виконати всі угоди. Цей процес перемикання потоків і пересилання даних від проміжного об'єкта до фактичного об'єкту і назад називається marshalling. Ця тема обговорюється в главі 6 моєї книги Developing ActiveX Components.

    В випадку DLL серверів, одиночна потокова модель вимагає, щоб всі об'єкти в сервер створювалися і викликалися у тому ж самому потоці що і перший об'єкт, створений сервером.

    Модель Apartment Threading

    Зверніть увагу, що модель Apartment Threading як визначено COM не вимагає, щоб кожен потік мав власний набір глобальних змінних. Visual Basic таким чином реалізує модель Apartment Threading. Модель Apartment Threading декларує, що кожний об'єкт може бути створений у власному потоці, проте, як тільки об'єкт створений, його методи і властивості можуть викликатися тільки тим же самим потоком, що створив об'єкт. Якщо об'єкт іншого потоку захоче мати доступ до методів цього об'єкту, то він повинен діяти через проміжний об'єкт.

    Така модель щодо легкою для реалізації. Якщо Ви виправили глобальні змінні (як робить Visual Basic), модель Apartment Threading автоматично гарантує безпеку потоку - так як кожен об'єкт справді виконується у власному потоці, і завдяки відсутності глобальних змінних, об'єкти в різних потоках не взаємодіють один з одним.

    Модель вільних потоків

    Модель вільних потоків (Free Threading Model) полягає в наступному .. Будь-який об'єкт може бути створений в будь-якому потоці. Всі методи і властивості будь-якого об'єкта можуть бути викликає в будь-який час з будь-якого потоку. Об'єкт приймає на себе всю відповідальність за обробку будь-якої необхідної синхронізації.

    Це найважча у реалізації модель, так як потрібно, щоб всю синхронізацію обробляв програміст. Фактично до недавнього часу, технологія OLE безпосередньо не підтримувала цю модель! Однак, з тих пір marshalling ніколи не потрібно і це найбільш ефективна модель потоків.

    Яку модель підтримує ваш сервер?

    Як програму або сама Windows дізнається, яку модель потоків використовує сервер? Ця інформація включена в системний реєстр (registry). Коли Visual Basic створює об'єкт, він перевіряє системний реєстр, щоб визначити, в яких випадках потрібно використовувати проміжний об'єкт (proxy object) і в яких - marshalling.

    Ця перевірка є обов'язком клієнта і необхідна для суворої підтримки вимог багатопоточності для кожного об'єкта, якого він створює.

    Функція API CreateThread

    Тепер давайте подивимося, як з Visual Basic може використовуватися функція API CreateThread. Скажімо, Ви маєте клас, що Ви хочете виполненять в іншому потоці, наприклад, щоб виконати деяку фонову операцію. Характерний клас такого типу міг би мати наступний код (з прикладу MTDemo 3):

    'Class clsBackground

    'MTDemo 3 - Multithreading example

    'Copyright © 1997 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, в якому показано ідентифікатор потоку, за яким ми можемо визначити потік, в якому виконується код. Замість VB команди MessageBox використовується MessageBox API, тому що функція API, як відомо, підтримує безпечне виконання потоків. Другий MessageBox з'являється після того, як закінчений підрахунок і Згенеровано подія, яке вказує, що операція завершена.

    Шпалери потік запускається за допомогою наступного коду у формі frmMTDemo3:

    Private Sub cmdCreateFree_Click ()

    Set c = New clsBackground

    StartBackgroundThreadFree c

    End Sub

    Функція StartBackgroundThreadFree визначена в модулі modMTBack наступним чином:

    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, 2000, 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 біт мінлива прапорів, яка дозволяє Вам управляти запуском потоку (активний, припинений і т.д.). Докладніше про ці прапорах можна почитати в Microsoft's online 32 bit reference.

    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, який описує цей підхід, містить дуже багато попереджень про те, що при використанні цього підходу є деякі речі, які не працюють.

    Деякі розробники, хто пробували розгортати додатки, що застосовують цей тип багатопоточності, виявили, що їх застосування викликають збої після оновлення до VB5 service pack 2.

    Є Чи це дефектом Visual Basic?

    означає Чи означає це, що Microsoft не забезпечила сумісність?

    Відповідь на обидва питання: Ні

    Проблема не в Microsoft або Visual Basic.

    Проблема полягає в тому, що вищезазначений код є сміттям.

    Проблема проста - Visual Basic підтримує об'єкти і в моделі одиночного потоку і в apartment model.

    Дозвольте мені перефразувати це: об'єкти Visual Basic є COM об'єктами і вони, згідно з COM угодою, будуть правильно працювати як в моделі одиночного потоку так і в apartment model. Це означає, що кожен об'єкт очікує, що будь-які виклики методів будуть відбуватися в тому ж самому потоці, який створив об'єкт.

    Приклад, показаний вище, порушує це правило.

    Це порушує угоду COM.

    Що це означає?

    Це означає, що поведінка об'єкта підпорядкованої змін, так як Visual Basic постійно модифікується.

    Це означає, що будь-яка спроба об'єкта звернутися до інших об'єктів або форм може потерпіти невдачу і що причини відмов можуть змінюватися, оскільки ці об'єкти модифікуються.

    Це означає, що навіть код, який зараз працює, може раптово викликала збій, оскільки інші об'єкти додаються, видаляються чи змінюються.

    Це означає, що неможливо характеризувати поведінку за або передбачити, чи буде воно працювати чи може чи працювати в будь-якій даному середовищі.

    Це означає, що неможливо передбачити, чи код працювати на будь-який даної системі, і що його поведінка може залежати від операційної, кількості використовуваних процесорів та інших проблем конфігурації системи.

    Ви бачите, що як тільки Ви порушуєте угоду COM, Ви більше не захищені тими можливостями COM, які дозволяють об'єктам успішно взаємодіяти один з одним і з клієнтами.

    Цей підхід є програмної алхімією. Це безвідповідально і жоден програміст не повинен коли-небудь використовувати це. Точка.

    Назад до функції API CreateThread

    Тепер, коли я показав Вам, чому підхід до використання CreateThread API, показаний в деяких статтях, є сміттям, я покажу Вам, як можна використовувати цю функцію API безпечно. Прийом простий -

    Ви повинні просто твердо притримати угоди COM про потоках. Це займе небагато більше часу і зусиль, але практика показала, що виходять дуже надійні результати.

    Приклад MTDEMO3 демонструє цей підхід у формі frmMTDemo3, що має код, який запускає клас фону в 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, 2000, AddressOf _

    BackgroundFuncApt, threadparam, 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

    Initialized = True

    End Sub

    Ви бачите, нам необхідний ідентифікатор інтерфейсу - 16 байтове структура, яка унікально визначає інтерфейс. У Зокрема нам необхідний ідентифікатор інтерфейсу для інтерфейсу IDispatch (детальна інформація щодо IDispatch може бути знайдена в моїй книзі Developing ActiveX Components). Функція 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) - відносно повільні операції. Ви фактично ніколи не захочете виконувати фонову операцію як показано тут.

    Але цей підхід може надзвичайно добре працювати, якщо Ви можете поміщати фонову операцію

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status