Введення p>
До моменту появи персональних комп'ютерів у світі існувалокілька технічних рішень, що дозволяють реалізувати багатозадачність навеликих машинах. У колишньому СРСР це були машини серії ЄС і болгарські Ізотов.
Вони теоретично дозволяли підключати до 255 терміналів, де кожномутерміналу виділялося деяку кількість ресурсів комп'ютера іпроцесорного часу. На практиці нормальна робота такого комплексузабезпечувалася за наявності не більше 25-30 терміналів, або менше прискладних завданнях. p>
Для персональних ЕОМ багатозадачність не вводилися принципово. Аджевиходячи з назви PC - "Personal Computer" передбачалося, що працюватибуде одна людина з одного поточної завданням. Як операційна системабула прийнята перероблена система CP/M під назвою MS-DOS. Вона так самоне передбачала багатозадачності. Основна проблема розробкибагатозадачного операційної системи це не реентерабільность її функцій. ТеТобто якщо один процес запустив функцію читання файлу, то інший процес незможе не тільки звертатися до файлів, а й взагалі викликати інші їїфункції. Для цього необхідна підтримка на рівні процесора яка булавведена з розробкою лінійки 286. p>
Багатозадачність і багатопоточність 1 p>
Режими багатозадачності 2
Багатозадачність в DOS 2
Невитесняющая багатозадачність 3
Presentation Manager і послідовна чергу повідомлень 6
Рішення, які використовують багатопоточність 7
Багаторівнева архітектура 9
Колізії, що виникають при використанні потоків 10
Переваги Windows 11
Нова удосконалена багатопотокова програма 13
Про використанні функції Sleep 13
Критичний розділ 14
Об'єкт Mutex 17
Повідомлення про події 18
Локальна пам'ять потоку 18 p>
Багатозадачність і багатопоточність p>
Багатозадачність (multitasking) - це здатність операційної системивиконувати декілька програм одночасно. В основі цього принципу лежитьвикористання операційною системою апаратного таймера для виділеннявідрізків часу (time slices) для кожного з одночасно виконуванихпроцесів. Якщо ці відрізки часу достатньо малі, і машина неперевантажена занадто великим числом програм, то користувачеві здається, щовсі ці програми виконуються паралельно. p>
багатозадачності Ідея не нова. Багатозадачність реалізується на великихкомп'ютерах типу мейнфрейм (mainframe), до яких підключені десятки, аіноді й сотні, терміналів. У кожного користувача, що сидить за екраномтакого термінала, створюється враження, що він має ексклюзивний доступдо всієї машині. Крім того, операційні системи мейнфреймов часто даютьможливість користувачам перевести завдання у фоновий режим, де вонивиконуються в той час, як користувач може працювати з іншогопрограмою. p>
Для того, щоб багатозадачність стала реальністю на персональнихкомп'ютерах, було потрібно досить багато часу. Але, здається, зараз минаближаємося до епохи використання багатозадачності на ПК (PC). Як мипобачимо незабаром, деякі розширені 16-розрядні версії Windowsпідтримують багатозадачність, а ті що є тепер в нашому розпорядженні
Windows NT і Windows 95 - 32-розрядні версії Windows, підтримують крімбагатозадачності ще й багатопоточність (multithreading). p>
Нить - це можливість програми самій бути багатозадачного.
Програма може бути розділена на окремі потоки виконання (threads),які, як здається, виконуються паралельно. На перший погляд цяконцепція може здатися чи корисною, але виявляється, що програмиможуть використовувати багатопоточність для виконання протяжних в часіоперацій у фоновому режимі, не змушуючи користувача надовго відриватися відмашини. p>
Режими багатозадачності p>
На зорі використання персональних комп'ютерів деякі відстоювалиідею багатозадачності для майбутнього, але багато хто ламав голову над питанням:яка користь від багатозадачності на однокористувацький машині? УНасправді виявилося, що багатозадачність - це саме те, щонеобхідно користувачам, навіть не підозрює про це. p>
Багатозадачність в DOS p>
Мікропроцесор Intel 8088, що використовувався в перших ПК, не бувспеціально розроблений для реалізації багатозадачності. Частково проблема
(як було показано в попередньому розділі) полягала в недоліках управлінняпам'яттю. У той час, як безліч програм починає і закінчує своєвиконання, багатозадачна операційна система повинна здійснюватипереміщення блоків пам'яті для об'єднання вільного простору. Напроцесорі 8088 це було неможливо реалізувати в стилі, прозорому длядодатків. p>
Сама DOS не могла тут чим-небудь істотно допомогти. Будучирозробленої таким чином, щоб бути маленькою і не заважати додаткам,
DOS підтримувала, крім завантаження програм та забезпечення їм доступу дофайлову систему, ще не так багато коштів. p>
Тим не менше, творчі програмісти, які працювали з DOS на зорі їїпояви, знайшли шлях подолання цих перешкод, переважно привикористанні резидентних (terminate-and-stay-resident, TSR) програм.
Деякі TSR-програми, такі як спулера друку, використовували перериванняапаратного таймера для виконання процесу у фоновому режимі. Інші,подібно спливаючим (popup) утилітам, таким як SideKick, могли виконуватиодне із завдань перемикання - припинення виконання програми на часроботи утиліти. DOS також була вдосконалена для забезпечення підтримкирезидентних програм. p>
Деякі виробники програмного забезпечення намагалися створитибагатозадачні оболонки або оболонки, що використовують перемикання міжзавданнями, як надбудова над DOS (наприклад, Quarterdeck's DeskView), алетільки один з цих оболонок одержала широке поширення на ринку.
Це, звичайно. Windows. P>
Невитесняющая багатозадачність p>
Коли Microsoft випустила на ринок Windows 1.0 в 1985 році, це булоще у великій мірі штучним рішенням, вигаданим для подоланняобмежень MS DOS. У той час Windows працювала в реальному режимі (realmode), але навіть тоді вона була здатна переміщати блоки фізичної пам'яті
(одна з необхідних умов багатозадачності) і робила це, хоч і не дужепрозоро для додатків, але все-таки цілком задовільно. p>
У графічної віконної середовищі багатозадачність набуває набагатобільше значення, ніж у однокористувацький операційній системі,використовує командний рядок. Наприклад, у класичній операційноїсистемі UNIX, що працює з командним рядком, існує можливістьзапускати програми з командного рядка так, щоб вони виконувалися вфоновому режимі. Однак, будь-який висновок на екран з програми повинен бутипереадресований у файл, інакше цей висновок змішається з поточним вмістомекрану. p>
Віконна оболонка дозволяє декількох програмах виконуватисяспільно, розділяючи один екран. Переключення вперед і назад стаєтривіальним, існує можливість швидко передавати дані з однієїпрограми в іншу, наприклад, розмістити картинку, створену в програмімалювання, в текстовому файлі, утвореному за допомогою текстового процесора.
Передача даних підтримувалася в різних версіях Windows: спочатку звикористанням папки обміну (clipboard), пізніше - за допомогою механізмудинамічного обміну даними (Dynamic Data Exchange, DDE), зараз - черезвпровадження та зв'язування об'єктів (Object Linking and Embedding, OLE). p>
І все ж, реалізована в ранніх версіях Windows багатозадачність НЕбула традиційною витісняючої, заснованої на виділення відрізків часу,як в розрахованих на багато користувачів операційних системах. Такі операційнісистеми використовують системний таймер для періодичного перериваннявиконання однієї задачі і запуску іншого. 16-розрядні версії Windowsпідтримували так звану невитесняющую багатозадачність (non-preemptivemultitasking). Такий тип багатозадачності був можливий завдяки заснованоїна повідомленнях архітектуру Windows. У загальному випадку, Windows-програмаперебувала в пам'яті і не виконувалася до тих пір, поки не отримувалаповідомлення. Ці повідомлення часто були прямим або непрямим результатомвведення інформації користувачем з клавіатури або миші. Після обробкиповідомлення програма повертала назад керування Windows. p>
16-розрядні версії Windows не мали можливості довільноперемикати управління з одного Windows-програми на іншу, грунтуючись накванти часу таймера. Переключення між завданнями відбувалося в момент,коли програма завершувала обробку повідомлення і повертала управління
Windows. Таку невитесняющую багатозадачність називають також кооперативноїбагатозадачністю (cooperative multitasking) тому, що вона вимагаєдеякого узгодження між додатками. Одна Windows-програма моглапаралізувати роботу всієї системи, якщо їй було потрібно багато часу дляобробки повідомлення. p>
Хоча невитесняющая багатозадачність була основним типом багатозадачностів 16-розрядних версіях Windows, деякі елементи витісняючої
(примітивної, preemptive) багатозадачності в них теж були присутні. p>
Windows використовувала витісняючу багатозадачність для виконання DOS -програм, а також дозволяла бібліотек динамічного компонування (DLL)отримувати переривання апаратного таймера для задач мультимедіа. p>
16-розрядні версії Windows мали деякі особливості, якідопомагали програмістам якщо не дозволити, то, принаймні, справитися зобмеженнями, пов'язаними з невитесняющей багатозадачністю. Найбільшвідомою є відображення курсора миші у вигляді пісочного годинника. Звичайно,це не вирішення проблеми, а тільки лише можливість дати знати користувачу,що програма зайнята виконанням протяжної під час роботи, і щосистема якийсь час буде недоступна. Іншим частковим рішенням євикористання системного таймера Windows, що дозволяє виконувати будь -які дії періодично. Таймер часто використовується в програмах типугодин і додатках, що працюють з анімацією. p>
Іншим рішенням з подолання обмежень невитесняющейбагатозадачності є виклик функції PeekMessage, як ми бачили впрограмі RANDRECT в розділі 4. Звичайно програма використовує виклик функції
GetMes-sage для отримання повідомлень з черги. Проте, якщо в даниймомент часу чергу повідомлень порожня, то функція GetMessage буде чекатинадходження повідомлення в чергу, а потім поверне його. Функція PeekMessageпрацює інакше - вона повертає керування програмі навіть у тому випадку,якщо немає повідомлень в черзі. Таким чином, виконання роботи, що вимагаєвеликих витрат часу, буде тривати до того моменту, поки в черзіне з'являться повідомлення для даної чи будь-якої іншої програми. p>
Presentation Manager і послідовна чергу повідомлень p>
Першою спробою фірми Microsoft (у співпраці з IBM) запровадитибагатозадачність у квазі-DOS/Windows оболонку була система OS/2 і
Presentation Manager (PM). Хоча OS/2, звичайно, підтримувала витісняючубагатозадачність, часто здавалося, що це витіснення не було перенесено в
PM. Справа в тому, що PM вибудовував в чергу повідомлення, що формуються врезультаті користувацького вводу від клавіатури або миші. Це означає,що PM не надає програмі таке користувальницьке повідомлення до тихпір, поки попереднє повідомлення, введене користувачем, не буде повністюоброблено. p>
Хоча повідомлення від клавіатури або миші - це тільки частина безлічіповідомлень, які може отримати програма в PM або Windows, більшістьінших повідомлень є результатом подій, пов'язаних з клавіатурою абомишею. Наприклад, повідомлення від меню команд є результатом виборупункту меню за допомогою клавіатури або миші. Повідомлення від клавіатури або мишіне буде оброблено до тих пір, поки не буде повністю обробленоповідомлення від меню. p>
Основна причина організації послідовної черги повідомленьполягає в тому, щоб відстежити всі дії користувача. Якщо будь-якаповідомлення від клавіатури або миші викликає перехід фокусу вводу від одноговікна до іншого, то таке повідомлення клавіатури має бути направлена довікно, на яке встановився фокус вводу. Таким чином, система не знає,в яке вікно передавати повідомлення на обробку до тих пір, поки не будеоброблено попереднє повідомлення. p>
В даний час прийнято угоду про те, що не повинно бутиможливості для якого-небудь однієї програми паралізувати роботу всієїсистеми, і що потрібно використовувати непослідовну чергу повідомлень,підтримувану системами Windows 95 і Windows NT. Якщо одна програмазайнята виконанням протяжної у часі операції, то існуєможливість перемкнути фокус вводу на інший додаток. p>
Рішення, які використовують багатопоточність p>
Вище було розглянуто Presentation Manager операційної системи OS/2тільки через те, що це була перша оболонка, яка підготуваласвідомість деяких ветеранів програмування під Windows (в тому числі іавтора) до введення багатопоточності. Цікаво, що обмежена підтримкабагатопоточності в РМ дала програмістам основну ідею організації програм,використовують багатопоточність. Хоча ці обмеження зараз переважноподолані в Windows 95, проте уроки, отримані під час роботи з більшобмеженими системами, залишаються актуальними і донині. p>
У многопоточной середовищі програми можуть бути розділені на частини,звані потоками виконання (threads), які виконуються одночасно.
Підтримка багатопоточності виявляється найкращим вирішенням проблемипослідовної черги повідомлень в РМ і набуває повне значення при їїреалізації в Windows 95. p>
У термінах програми "потік" - це просто функція, яка може такожвикликати інші функції програми. Програма починає виконуватися зі свогоголовного (первинного) потоку, який в традиційних програмах на мові Сє функцією main, а у Windows-програми - WinMain. Будучивиконуваної, функція може створювати нові потоки обробки, виконуючисистемний виклик із зазначенням функції ініціалізації потоку (initial threadingfunction). Операційна система в витісняючої режимі перемикання керуванняміж потоками подібно до того, як вона це робить з процесами. p>
У РМ системи OS/2 будь-який потік може або створювати чергу повідомлень,або не створювати. РМ-потік повинен створювати чергу повідомлень, якщо вінзбирається створювати вікно. З іншого боку, потік може не створюватичергу повідомлень, якщо він здійснює тільки обробку даних абографічний висновок. Оскільки потоки, які не створюють черги повідомлень, необробляють повідомлення, то вони не можуть привести до "зависання" системи. Напотік, що не має черги повідомлень, накладається тільки одне обмеження
- Він не може посилати асинхронне повідомлення у вікно потоку, що маєчергу повідомлень, або викликати будь-яку функцію, якщо це призведе допосилці повідомлення. (Проте ці потоки можуть посилати синхронні повідомленняпотоків, що мають чергу повідомлень.) p>
Таким чином, програмісти, які працювали з РМ, навчилися розбивати своїпрограми на один потік з чергою повідомлень, що створює всі вікна іобробляє повідомлення для них, і один або декілька потоків, які не маютьчерг повідомлень, і виконують тривалі дії у фоновомурежимі. Крім того, програмісти, які працювали з РМ, дізналися про "правилі 1/10секунди ". Воно полягає в тому, що потік з чергою повідомлень витрачає не більше
1/10 секунди на обробку будь-якого повідомлення. Все, що вимагає більшоїчасу, слід виділяти в окремий потік. Якщо всі програмістидотримувалися цього правила, то ніяка РМ-програма не могла викликатизависання системи більш ніж на 1/10 секунди. p>
Багаторівнева архітектура p>
Як вже зазначалося вище, обмеження РМ дали програмістам основніідеї для розуміння того, як використовувати безліч потоків у програмі,виконуваної в графічному середовищі. Нижче наведені наші рекомендації зархітектурі багатопоточних програм: первинний або головний (primary) потіквашої програми створює всі вікна і відповідні їм віконні процедури,необхідні в програмі і обробляє всі повідомлення для цих вікон. Всіінші потоки - це просто фонові завдання. Вони не мають інтерактивноїзв'язку з користувачем, окрім як через первинний потік. p>
Один зі способів досягти цього полягає в тому, щоб первинний потікобробляв для користувача введення та інші повідомлення, можливо створюючи прице вторинні (secondary) потоки в процесі. Ці вторинні потокивиконують не пов'язані з користувачем завдання. p>
Іншими словами, первинний потік вашої програми єгубернатором, а вторинні потоки - почтом губернатора. Губернатор доручаєвсю велику роботу своїм помічникам на той час, поки він здійснюєконтакти із зовнішнім світом. Оскільки вторинні потоки є членамипочту, вони не можуть проводити свої прес-конференції. Вони скромно виконуютькожен своє завдання, роблять звіт губернаторові і чекають нових вказівок. p>
Потоки всередині окремої програми є частинами одного процесу,тому вони поділяють усі ресурси процесу, такі як пам'ять і відкритіфайли. Оскільки потоки поділяють пам'ять, відведену програмі, то вониподіляють і статичні змінні. Проте, у кожного потоку є свійвласний стек, і виходить, автоматичні змінні є унікальнимидля кожного потоку. Кожен потік, також, має свій стан процесора,яке зберігається і відновлюється при перемиканні між потоками. p>
Колізії, що виникають при використанні потоків p>
Власне розробка, програмування та налагодження складногобагатопотокового додатки є, природно, найскладнішими завданнями,з якими доводиться стикатися програмісту для Windows. Оскільки всистемі з витісняючої багатозадачністю потік може бути перервано в будь-якіймомент для перемикання на інший потік, то може випадково статися будь-якийнебажану взаємодію між двома потоками. p>
Однією з основних помилок у багатопоточних програмах є такзване стан перегонів (race condition). Це трапляється, якщопрограміст вважає, що один потік закінчить виконання своїх дій,наприклад, підготовку яких-небудь даних, до того, як ці дані будуть потрібнііншому потоку. Для координації дій потоків операційним системамнеобхідні різні форми синхронізації. Однією з таких форм єсемафор (semaphore), який дозволяє програмісту призупинитивиконання потоку в конкретній точці програми до тих пір, поки він неотримає від іншого потоку сигнал про те, що він може відновити роботу.
Схожі на семафори критичні розділи (critical sections), якіявляють собою розділи коду, під час виконання якого, потік неможе бути перерваний. p>
Але використання семафорів може призвести до іншої поширеноїпомилку, пов'язану з потоками, яка називається тупиком (deadlock). Цетрапляється, коли два потоки блокують виконання один одного, а для того,щоб їх розблокувати необхідно продовжити роботу. p>
На щастя, 32-розрядні програми більш стійкі до певнихпроблем, включаючи проблеми з потоками, ніж 16-розрядні програми.
Наприклад, припустимо, що один потік виконує просте дію: p>
lCount + +; p>
де ICount - 32-розрядна глобальна змінна типу довге ціле,яка використовується іншими потоками. У 16-розрядної програмі, в якій такийоператор мови З транслюється в два інструкції машинного коду (спочаткуінкрементіруется молодші 16 розрядів, а потім додається перенесення в старші
16 розрядів). Припустимо, що операційна система перервала потік між цимидвома інструкціями машинного коду. Якщо змінна ICount мала значення
$ 0000FFFF, то після виконання першого інструкції машинного коду ICountбуде мати нульове значення. Якщо в цей момент відбудеться перериванняпотоку, то інший потік отримає нульове значення змінної ICount. Тількипісля закінчення цього потоку значення ICount буде збільшено на одиницю досвого справжнього значення $ 00010000. p>
Такого роду помилка може бути ніколи не виявлена, оскільки доситьрідко призводить до проблем під час виконання. Для 16-розрядних програмнайкращий шлях запобігти таку помилку - це помістити даний виразу критичний розділ, в рамках якого потік не може бути перерваний. У 32 --розрядної програмі, однак, наведене вираз є абсолютнокоректним, оскільки воно компілюється в одну інструкцію машинного коду. p>
Переваги Windows p>
Операційні системи Windows 95 і Windows NT не мають послідовноїчерги повідомлень. Таке рішення здається дуже гарним: якщо програмавиконує тривалу обробку повідомлення, то курсор миші приймає формупісочного годинника при розташуванні над вікном цієї програми, і змінюється назвичайну стрілку, якщо він розташовується над вікном іншої програми. Простимклацанням кнопкою миші можна перевести інше вікно на передній план. p>
Проте, користувач як і раніше не може працювати з додатком,виконують тривалу операцію, оскільки виконання тривалої операціїзапобігає отримання повідомлень програмою. А це небажано. Програмаповинна бути завжди відкрита для повідомлень, а це вимагає використаннявторинних потоків. p>
У Windows 95 і Windows NT не існує відмінності між потоками,що мають чергу повідомлень, і потоками без черги повідомлень. При створеннікожен потік отримує свою власну чергу повідомлень. Це знижує числообмежень, що існують для потоків в РМ-програмі. (Проте, вбільшості випадків все ще обробка введення і повідомлень здійснюється водному потоці, а протяжні в часі завдання передаються іншим потокам,які не створюють вікон.) Така схема організації програми, як мипобачимо, майже завжди є найбільш розумною. p>
У Windows 95 і Windows NT є функція, яка дозволяє одномупотоку знищити інший потік, що належить тому ж процесу. Як вивиявите, коли почнете писати багатопотокові додатки під Windows,іноді це дуже зручно. Ранні версії операційної системи OS/2 немістили функції для знищення потоків. p>
Windows 95 і Windows NT підтримують так звану локальну пам'ятьпотоку (thread local storage, TLS). Для того щоб зрозуміти, що це таке,згадаємо про те, що статичні змінні, як глобальні так і локальніпо відношенню до функцій, розділяються між потоками, оскільки вонирозташовані в зоні пам'яті даних процесу. Автоматичні змінні
(що є завжди локальними по відношенню до функції) - унікальні длякожного потоку, тому що вони розташовуються в стеку, а кожен потік має свійстек. p>
Іноді буває зручно використовувати для двох і більше потоків одну і туж функцію, а статичні дані будуть використані унікальні для кожногопотоку. Це і є приклад використання локальної пам'яті потоку. Існуєкілька викликів функцій Windows для роботи з локальною пам'яттю потоку.
Фірма Microsoft ввела розширення в компілятор С, яке дозволяєвикористовувати локальну пам'ять потоку більш прозорим для програмістачином. p>
Нова удосконалена багатопотокова програма p>
Іноді має місце тенденція використовувати в програмі кожнуможливість, що пропонується операційною системою. Немає сенсу використовуватибезліч потоків у програмі, яка цього не потребує. Якщо програмавиводить на екран курсор у вигляді пісочного годинника на досить довгий періодчасу, або, якщо вона використовує функцію PeekMessage для того, щобуникнути появи курсору у вигляді пісочного годинника, то тоді ідеяреструктуризації програми в багатопотокове, ймовірно, може виявитисягарною. В іншому випадку, ви тільки ускладните собі роботу і, можливо,внесете в програму нові помилки. p>
Є навіть деякі ситуації, коли поява курсор миші у виглядіпісочного годинника, може бути зовсім підходящим. Вище вже згадувалося
"правило 1/10 секунди". Так от, завантаження великого файлу в пам'ять можепотребувати більше часу, ніж 1/10 секунди. Чи означає це, що функціїзавантаження файлу повинні були бути реалізовані з використанням поділу напотоки? Зовсім необов'язково. Коли користувач дає програмі командувідкрити файл, то він або вона зазвичай хоче, щоб операційна системавиконала її негайно. Виділення процесу завантаження файлу в окремийпотік просто призведе до ускладнення програми. Не варто це робити навіть зарадитого, щоб похвалитися перед друзями, що ви пишете багатопотоковідодатки. p>
Про використанні функції Sleep p>
Вище було показано, як краще організувати архітектуру програми,використовує багатопоточність, а саме, щоб первинний потік створював всівікна в програмі, містив всі віконні процедури цих вікон і обробляввсі повідомлення. Вторинні потоки виконують фонові завдання або завдання,протяжні в часі. p>
Однак, припустимо, що потрібно реалізувати анімацію у вторинномупотоці. Зазвичай анімація в Windows здійснюється з використанням повідомлення
WM_TIMER. Але якщо вторинний потік не створює вікно, то він не може отриматице повідомлення. А без завдання певного темпу анімація могла бздійснюватися занадто швидко. p>
Рішення полягає у використанні функції Sleep. Потік викликає функцію
Sleep для того, щоб добровільно відкласти своє виконання. Єдинийпараметр цієї функції - час, що задається в мілісекундах. Функція Sleep НЕздійснює повернення до тих пір, поки не закінчиться зазначений час. Упротягом нього виконання потоку призупиняється і виділення для ньогопроцесорного часу не відбувається (хоча очевидно, що для потоку все-такипотрібен якийсь незначний час, за який система повиннавизначити, пора відновлювати виконання потоку чи ні). p>
Якщо параметр функції Sleep задано рівним нулю, то потік буде позбавленийзалишку виділеного йому кванта процесорного часу. p>
Коли потік викликає функцію Sleep, затримка на заданий часвідноситься до цього потоку. Система продовжує виконувати інші потоки цьогота інших процесів p>
Критичний розділ p>
У однозадачной операційній системі звичайні програми не потребують
"світлофорах" для координації їх дій. Вони виконуються так, як нібивони є господарями дороги, по якій вони слідують. Не існуєнічого, що могло б втрутитися в те, що вони роблять. p>
Навіть у багатозадачного операційної системи більшість програмвиконуються незалежно один від одного. Але деякі проблеми все ж таки можутьвиникнути. Наприклад, двох програмах може знадобитися читати і писати водин файл в один і той же час. Для таких випадків операційна системапідтримує механізм поділу файлів (shared files) і блокуванняокремих фрагментів файлу (record locking). p>
Однак, в операційній системі, що підтримує багатопоточність, такерішення може внести плутанину і створити потенційну небезпеку. Поділ p>
даних між двома і більше потоками є загальним випадком. Наприклад,один потік може оновлювати одну або більше змінних, а інший можевикористовувати ці змінні. Іноді в цій ситуації може виникнутипроблема, а іноді - ні. (Пам'ятайте, що операційна система можеперемикати управління потоками тільки між інструкціями машинного коду.
Якщо просте ціле число ділиться між двома потоками, то змінацієї змінної зазвичай здійснюється однією інструкцією машинного коду, іпотенційні проблеми зводяться до мінімуму.) p>
Однак, припустимо, що потоки поділяють кілька змінних абоструктуру даних. Часто ці складні змінні або поля структур данихповинні бути узгодженими між собою. Операційна система можепереривати потік в середині процесу оновлення цих змінних. У цьомувипадку потік, який потім використовує ці змінні, буде мати справу знеузгодженими даними. p>
У результаті б виникла колізія, і неважко уявити собі, яктакого роду помилка може призвести до краху програми. У цій ситуації намнеобхідно щось схоже на світлофор, який міг би синхронізувати йкоординувати роботу потоків. Таким засобом і є критичнийрозділ. Критичний розділ - це блок коду, при виконанні якої потікне може бути перерваний. p>
Є чотири функції для роботи з критичними розділами. Щоб їхвикористовувати, вам необхідно визначити об'єкт типу критичний розділ,який є глобальною змінною типу CRITICAL_SECTION. Наприклад,
CRITICAL_SECTION CS; p>
Тип даних CRITICAL_SECTION є структурою, але її полявикористовуються тільки всередині Windows. Об'єкт типу критичний розділ спочаткуповинен бути ініціалізований одним з потоків програми за допомогою функції:
InitializeCriticalSection (& cs); p>
Ця функція створює об'єкт критичний розділ з ім'ям cs. Удокументації міститься наступне попередження: "Об'єкт критичнийрозділ не може бути переміщений або скопійований. Процес також не повиненмодифіковані об'єкт, а повинен поводитися з ним, як з "чорним ящиком "." p>
Після ініціалізації об'єкту критичний розділ потік входить докритичний розділ, викликаючи функцію:
EnterCriticalSection (& cs); p>
У цей момент потік стає власником об'єкта. Два різніпотоку не можуть бути власниками одного об'єкта критичний розділодночасно. Отже, якщо один потік увійшов у критичний розділ, тонаступний потік, викликаючи функцію EnterCriticalSection з тим же самимоб'єктом p>
критичний розділ, буде затриманий усередині функції. Повернення з функціївідбудеться тільки тоді, коли перший потік покине критичний розділ,викликавши функцію:
LeaveCriticalSection (& cs); p>
У цей момент другий потік, затриманий у функції
EnterCriticalSection, стане власником критичного розділу, і йоговиконання буде відновлено. p>
Коли об'єкт критичний розділ більше не потрібен вашій програмі, йогоможна видалити за допомогою функції:
DeleteCriticalSection (& cs); p>
Це призведе до звільнення всіх ресурсів системи, задіяних дляпідтримки об'єкта критичний розділ. p>
Механізм критичних розділів заснований на принципі взаємного виключення
(mutual exclusion). Цей термін нам ще зустрінеться при подальшомурозгляді синхронізації потоків. Тільки один потік може бути власникомкритичного розділу в кожен конкретний момент часу. Отже,один потік може увійти в критичний розділ, встановити значення полівструктури та вийти з критичного розділу. Другий потік, який використовує цюструктуру, також міг би увійти в критичний розділ перед здійсненнямдоступу до полів структури, а потім вийти з критичного розділу. p>
Зверніть увагу, що можливе визначення декількох об'єктів типукритичний розділ, наприклад, cs1 і cs2. Якщо в програмі є чотирипотоку, і два перших з них поділяють деякі дані, то вони можутьвикористовувати перший об'єкт критичний розділ, а два інших потоку, такожподіляють інші дані, можуть використовувати другий об'єкт критичнийрозділ. p>
Зверніть увагу, що треба бути дуже обережним при використаннікритичного розділу в головному потоці. Якщо вторинний потік проводитьзанадто багато часу в його власному критичному розділі, то це можепривести до "зависання" головного потоку на надто великий період часу. p>
Об'єкт Mutex p>
Існує одне обмеження у використанні критичних розділів. Вонополягає в тому, що їх можна застосовувати для синхронізації потоків тількив рамках одного процесу. Але бувають випадки, коли необхідносинхронізувати дії потоків різних процесів, які розділяютьбудь-які ресурси (наприклад, пам'ять). Використовувати критичні розділи втакій ситуації не можна. Замість них підключаються об'єкти типу mutex (mutexobject). p>
Складений слово "mutex" походить з словосполучення "mutualexclusion ", що означає взаємне виключення, і дуже точно відображаєпризначення об'єктів. Ми хочемо попередити можливість переривання потоку впрограмі до тих пір, поки не буде виконано оновлення або використанняподілюваних даних. p>
Повідомлення про події p>
Ми можемо визначити поняття великої роботи як дії, виконуючиякі, програма порушить "правило 1/10 секунди". Прикладами великої роботиможуть служити: перевірка орфографії в текстових процесорах, сортування ііндексування файлів баз даних, перерахунок електронної таблиці, друк інавіть складне малювання. Звичайно, як ми вже знаємо, краще рішення полягає впроходженні "правилом 1/10 секунди", тобто в передачі великої роботивторинним потокам обробки. Ці вторинні потоки не створюють вікон і,значить, не обмежені "правилом 1/10 секунди". p>
Часто буває, що вторинному потоку треба проінформувати первиннийпотік про те, що він завершився, або первинного потоку треба перервати роботу,виконувану вторинним потоком. p>
Локальна пам'ять потоку p>
Глобальні змінні в багатопоточних програмах (так само як і будь-якавиділена пам'ять) розділяються між всіма потоками в програмі. Локальністатичні змінні функцій також розділяються між всіма потоками,що використовують цю функцію. Локальні автоматичні змінні в функціїє унікальними для кожного потоку, тому що вони зберігаються в стеку, акожен потік має свій власний стек. p>
Може виникнути необхідність мати постійну область пам'яті,унікальну для кожного потоку. Наприклад, функція strtok мови С, якавже згадувалася в цьому розділі, вимагає такого типу пам'ять. Немає сумнівів, що
З його не підтримує. У Windows 95 є чотири функції, що підтримуютьцю пам'ять, яка називається локальною пам'яттю потоку (thread localstorage, TLS). p>
Первинний потік викликає функци?? JTsAlloc для отримання значенняіндексу:dwTlsIndex = TIsAlloc (); p>
Він може зберігатися в глобальній змінній або може бути переданийфункції потоку в параметрі-структурі. p>
Функція потоку починається з виділення пам'яті для структури даних і звиклику функції TIsSetValue, використовуючи індекс, отриманий раніше:
TIsSetValue (dwTlsIndex, GlobalAlloc (GPTR, sizeof (DATA))); p>
Ця дія встановлює відповідність покажчика з конкретним потокомі конкретним індексом в потоці. Тепер, будь-яка функція, яку потрібновикористовувати цей покажчик (включаючи саму базову функцію потоку), можевикористовувати код, подібний до такого:
PDATA pdata;pdata = (PDATA) TIsGetValue (dwTlsIndex); p>
Тепер вона може змінювати значення pdata-> a і pdata-> b. Передзавершенням функції потоку необхідно звільнити захоплену пам'ять:
GlobalFree (TIsGetValue (dwTlsIndex)); p>
Коли всі потоки, які використовують ці дані будуть завершені, первиннийпотік звільняє індекс:
TIsFree (dwTlsIndex); p>
Корисно подивитися як організована локальна пам'ять потоку. (Меніневідомо, як насправді Windows 95 це робить, але описуванасхема цілком правдоподібна.) По-перше, функція TIsAlloc могла б простоблок виділити пам'яті (завдовжки 0 байт) і повернути значення індексу, щоє покажчиком на цей блок. Кожного разу при виконанні функції TIsSet
Value з цим індексом блок пам'яті збільшується на 8 байт за допомогою функції
GlobalReAlloc. У цих 8 байтах зберігаються ідентифікатор потоку, що викликаєфункцію, отриманий за допомогою функції GetCurrentThreadID, і покажчик,переданий функції TIsSetValue. Функція TIsGetValue просто використовуєідентифікатор потоку для пошуку в таблиці, а потім повертає вказівник.
Функція TZsFree звільняє блок пам'яті. P>
Реалізація багатопоточності в Delphi p>
Стандартний майстер модулів в Delphi автоматично створює модульщо містить клас потоку із зазначеним ім'ям. Весь код який необхідновинести в окремий потік поміщається в метод класу Execute. p>
Базовий клас для створення потоку користувача - TThread
TThread = classprotected procedure DoTerminate; virtual; procedure Execute; virtual; procedure Synchronize (Method: TThreadMethod); property ReturnValue: Integer; property Terminated: Boolean; public constructor Create (CreateSuspended: Boolean); procedure Resume; procedure Suspend; procedure Terminate; function WaitFor: LongWord; property FreeOnTerminate: Boolean; property Handle: Thandle; property Priority: TthreadPriority; property Suspended: Boolean; property ThreadID: Thandle; property OnTerminate: TnotifyEvent;end; p>
Процес, що породив потік може гнучко управляти його станом:пріоритетом Priority; призупинити і продовжити його виконання, а так самодостроково завершити виконання потоку. p>
Для виклику методів VCL необхідно синхронізувати дочірній потік зголовним. Для цього є процедура Synchronize (Method: TThreadMethod); p>
unit Unit1;interfaceuses
Classes;type
TSamples = class (TThread) private p>
(Private declarations) protected procedure Execute; override; end; p>
implementation p>
(Підказка Delphi з приводу Synchronize. p>
Important: Methods and properties of objects in VCL can only be used in a method called using Synchronize, for example, p>
Synchronize (UpdateCaption); p>
and UpdateCaption could look like, p>
procedure Samples.UpdateCaption; begin p>
Form1.Caption: = 'Updated in a thread'; end;) p>
( Samples) p>
procedure TSamples.Execute;begin
(Тут має бути розміщений код потоку)end; p>
end. p>
Список використаної літератури p>
1. Turbo Pascal for Windows в 2-х томах. Нейл Рубенкінг Пер. з англ. - P>
М.: Світ, 1993, 536 с., Іл. P>
2. Теорія та практика C