48837 (666099), страница 3

Файл №666099 48837 (Робота з "потоками" в середовищі Delphi) 3 страница48837 (666099) страница 32016-07-31СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 3)

Перераховані вище засоби синхронізації в основному інкапсульовані до складу класів Delphi. У програміста є дві альтернативи. З одного боку, до складу бібліотеки VCL включений модуль SYNCOBJS.PAS, що містить класи для події (TEvent) і критичної секції (TCriticalSection). З іншою, з Delphi поставляється відмінний приклад IPCDEMOS, який ілюструє проблеми взаємодії процесів і містить модуль IPCTHRD.PAS з аналогічними класами – для тієї ж події, взаємного виключення (TMutex), а також спільно використовуваної пам'яті (TSharedMem).

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

Подія

Об'єкт типу подія (event) – простий вибір для задач синхронізації. Він подібний дверному дзвінку – дзвенить до тих пір, поки його кнопка знаходиться в натиснутому стані, сповіщаючи про цей факт оточуючих. Аналогічно, і об'єкт може бути в двох станах, а «чути» його можуть багато потоків відразу. Клас TEvent (модуль SYNCOBJS.PAS) має два методи: setEvent і ResetEvent, які переводять об'єкт в активний і пасивний стан відповідно. Конструктор має наступний вигляд:

constructor Create (EventAttributes: PSecurityAttributes;

ManualReset, InitialState: Boolean; const Name: string);

Тут параметр initialstate – початковий стан об'єкту, ManualReset – спосіб його скидання (перекладу в пасивний стан). Якщо цей параметр рівний True, подія повинна бути скинуте уручну. Інакше подія скидається у міру того, як стартує хоч один потік, що чекав даного об'єкту.

На третьому методі:

TWaitResult = (wrSignaled, wrTimeout, wrAbandoned, wrError);

function WaitFor (Timeout: DWORD): TWaitResult;

зупинимося докладніше. Він дає можливість чекати активізації події протягом Timeout мілісекунд. Як ви могли здогадатися, усередині цього методу відбувається виклик функції waitFotsingieObject. Типових результатів на виході waitFor два – wrsignaied, якщо відбулася активізація події, і wrTimeout, якщо за час тайм-ауту нічого не відбулося.

Примітка

Якщо потрібно (і допустимо!) чекати нескінченно довго, слід встановити параметр Timeout в значення INFINITE.

Розглянемо маленький приклад. Включимо до складу нового проекту об'єкт типа TThread, наповнивши його метод Execute наступним вмістом:

Var res: TWaitResult;

procedure TSimpleThread. Execute;

begin

e:= TEvent. Create (nil, True, false, 'test');

repeat

e. ReSetEvent;

res:= e. WaitFor(10000);

Synchronize(Showlnfo);

until Terminated; e. Free;

end;

procedure TSimpleThread. Showlnfo;

begin

ShowMessage (IntToStr(Integer (res)));

end;

На головній формі розмістимо дві кнопки – натиснення однієї з них запускає потік, натиснення другої активізує подію:

procedure TForml. ButtonlClick (Sender: TObject);

begin

TSimpleThread. Create(False);

end;

procedure TForml. Button2Click (Sender: TObject);

begin

e. SetEvent;

end;

Натиснемо першу кнопку. Результат (метод Showlnfo), що тоді з'явився на екрані, залежатиме від того, чи була натиснута друга кнопка або закінчилися відведені 10 секунд.

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

таких процедур відносяться відкладене (overlapped) введення / висновок і події, пов'язані з комунікаційними портами.

Взаємні виключення

Об'єкт типу взаємне виключення (mutex) дозволяє тільки одному потоку зараз володіти ним. Якщо продовжувати аналогії, то цей об'єкт можна порівняти з естафетною паличкою.

Клас, що інкапсулює взаємне виключення, – TMutex – знаходиться в модулі IPCTHRD.PAS (приклад IPCDEMOS). Конструктор:

constructor Create (const Name: string);

задає ім'я створюваного об'єкту. Спочатку він не належить нікому. (Але функція API createMutex, що викликається в ньому, дозволяє передати створений об'єкт тому потоку, в якому це відбулося.) Далі метод

function Get (TimeOut: Integer): Boolean;

виробляє спробу протягом Timeout мілісекунд заволодіти об'єктом (в цьому випадку результат рівний True). Якщо об'єкт більш не потрібен, слід викликати метод

function Release: Boolean;

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

Семафор

Семафор (semaphore) подібний взаємному виключенню. Різниця між ними у тому, що семафор може управляти кількістю потоків, які мають до нього доступ. Семафор встановлюється на граничне число потоків, яким доступ дозволений. Коли це число досягнуте, подальші потоки будуть припинені, поки один або більш потоків не від'єднаються від семафора і не звільнять доступ.

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

Критична секція

Працюючи в Delphi, програміст може також використовувати об'єкт типу критична секція (critical section). Критичні секції подібні взаємним виключенням по суті, проте між ними існують дві головні відмінності:

  • взаємні виключення можуть бути спільно використані потоками в різних процесах, а критичні секції – ні;

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

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

Візьмемо клас TCriticalSection (модуль SYNCOBJS.PAS). Логіка використовування його проста – «тримати і не пущать». У багатопотоковому додатку створюється і ініціалізується загальна для всіх потоків критична секція. Коли один з потоків досягає критично важливої ділянки коду, він намагається захопити секцію викликом методу Enter:

MySection. Enter; try DoSomethingCritical;

finally

MySection. Leave;

end;

Коли інші потоки доходять до оператора захоплення секції Enter і знаходять, що вона вже захоплена, вони припиняються аж до звільнення секції першим потоком шляхом виклику методу Leave. Зверніть увагу, що виклик Leave поміщений в конструкцію try..finally – тут потрібна стовідсоткова надійність. Критичні секції є системними об'єктами і підлягають обов'язковому звільненню – втім, як і решта об'єктів, що розглядаються тут.

Процес. Породження дочірнього процесу

Об'єкт типу процес (process) може бути використаний для того, щоб припинити виконання потоку в тому випадку, якщо він для свого продовження потребує завершення процесу. З практичної точки зору така проблема встає, коли потрібно в рамках вашого додатку виконати додаток, створений кимось іншим, або, наприклад, сеанс MS-DOS.

Розглянемо, як, власне, один процес може породити інший. Замість застарілої і підтримуваної тільки для сумісності функції winExec, що перекочувала з колишніх версій Windows, набагато правильніше використовувати могутнішу:

function CreateProcess (IpApplicationName: PChar; IpCorranandLine: PChar;

IpProcessAttributes, IpThreadAttributes: PSecurityAttributes;

blnheritHandles: BOOL;

dwCreationFlags: DWORD; IpEnvironment: Pointer;

IpCurrentDirectory: PChar;

const IpStartupInfo: TStartupInfo;

var IpProcessInformation: TProcessInformation): BOOL;

Перші два параметри ясні – це ім'я додатку, що запускається, і передавані йому в командному рядку параметри. Параметр dwCreationFlags містить прапори, що визначають спосіб створення нового процесу і його майбутній пріоритет. Використані в приведеному нижче лістингу прапори означають: CREATE_NEW_CONSOLE – будет запущено новий консольний додаток з окремим вікном; NORMAL_PRIORITY_CLASS – нормальний пріоритет.

Структура TStartupInfo містить відомості про розмір, колір, положення вікна створюваного додатку. У нижченаведеному прикладі (лістинг 29.1) використовується поле wshowwindow: встановлений прапор SW_SHOWNORMAL, що означає візуалізацію вікна з нормальним розміром.

На виході функції заповнюється структура IpProcessInformation. У ній програмісту повертаються дескриптори і ідентифікатори створеного процесу і його первинного потоку. Нам знадобиться дескриптор процесу – в нашому прикладі створюється консольний додаток, потім відбувається очікування його завершення. «Просигналить» нам про це саме об'єкт IpProcessInformation.hProcess.

Лістинг 29.1. Породження дочірнього процесу

var

IpStartupInfo: TStartupInfo;

IpProcessInformation: TProcessInformation;

begin

FillChar (IpStartupInfo, Sizeof(IpStartupInfo), 10);

IpStartupInfo.cb:= Sizeof(IpStartupInfo);

IpStartupInfo.dwFlags:= STARTFJJSESHOWWINDOW; IpStartupInfo.wShowWindow:= SW_SHOWNORMAL;

if not CreateProcess (nil,

PChar ('ping localhost'),

nil,

nil,

false,

CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,

nil,

nil,

IpStartupInfo, IpProcessInformation) then

ShowMessage (SysErrorMessage(GetLastError;)

else

begin

WaitForSingleObject

(IpProcessInformation.hProcess, 10000); CloseHandle (IpProcessInformation.hProcess);

end;

end;

Потік

Потік може чекати інший потік точно так, як і інший процес. Очікування можна організувати за допомогою функцій API (як в тільки що розглянутому прикладі), але зручніше це зробити за допомогою методу TThread. WaitFor.

Консольне введення

Консольне введення (console input) годиться для потоків, які повинні чекати відгуку на натиснення користувачем клавіші на клавіатурі. Цей тип очікування може бути використаний в програмі дуплексного зв'язку (chat). Один потік при цьому чекатиме отримання символів; другий – відстежувати введення користувача і потім посилати набраний текст чекаючому додатку.

Сповіщення про зміну у файловій системі

Цей вид об'єкту очікування дуже цікавий і незаслужено мало відомий. Ми розглянули практично всі варіанти того, як один потік може подати сигнал іншому. А як одержати сигнал від операційної системи? Ну, наприклад, про те, що у файловій системі відбулися якісь зміни? Такий вид сповіщення з ОС UNIX і доступний програмістам, що працюють з Win32. Для організації моніторингу файлової системи потрібно використовувати

Три функції – FindFirstChangeNotification, FindNextChangeNotification і FinddoseChangeNotification. Перша з них повертає дескриптор об'єкту файлового сповіщення, який можна передати у функцію очікування. Об'єкт активізується тоді, коли в заданій теці відбулися ті або інші зміни (створення або знищення файлу або теки, зміна прав доступу і т. д.). Друга – готує об'єкт до реакції на наступну зміну. Нарешті, за допомогою третьої функції слід закрити той, що став непотрібним об'єкт.

Так може виглядати код методу Execute потоку, створеного для моніторингу файлової системи:

var DirName: string;

procedure TSimpleThread. Execute;

var r: Cardinal;

fn: THandle;

begin

fn:= FindFirstChangeNotification (pChar(DirName), True,

FILEJTOTIFY_CHANGE_FILE_NAME);

repeat

r:= WaitForSingleObject (fn, 2000);

if r = WAIT_OBOECT_0 then

Synchronize (Forml. UpdateList);

if not FindNextChangeNotification(fn) then

break;

until Terminated;

FindCloseChangeNotification(fn);

end;

На головній формі повинні знаходитися компоненти, потрібні для вибору обстежуваної теки, а також компонент TListBox, в який записуватимуться імена файлів:

procedure TForml. ButtonlClick (Sender: TObject);

var dir: string; begin

if SelectDirectory (dir, [], 0)

then begin

Editl. Text:= dir; DirName:= dir;

end;

end;

procedure TForml. UpdateList;

var SearchRec: TSearchRec;

begin

ListBoxl. Clear;

FindFirst (Editl. Text+'\*.*', faAnyFile, SearchRec); repeat ListBoxl. Items. Add (SearchRec. Name);

until FindNext(SearchRec)<> 0;

FindClose(SearchRec);

end;

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

Локальні дані потоку

Цікава проблема виникає, якщо в додатку буде декілька однакових потоків. Як уникнути сумісного використовування одних і тих же змінних декількома потоками? Перше, що спадає на думку, – додати і використати поля об'єкту – нащадка TThread, які можна додати при його створенні. Кожен потік відповідає окремому екземпляру об'єкту, і їх дані перетинатися не будуть. (До речі, це одна з великих зручностей використовування класу TThread.) Але є функції API, які знать не знають про об'єкти Delphi і їх поля і властивості. Для підтримки розділення даних між потоками на нижньому рівні в мову Object Pascal введена спеціальна директива – threadvar, яка відрізняється від директиви опису змінних var тим, що застосовується тільки до локальних даних потоку. Наступний опис:

Var

datal: Integer; threadvar

data2: Integer;

означає, що змінна datal використовуватиметься всіма потоками даного додатку, а змінна data2 буде у кожного потоку своя.

Як уникнути одночасного запуску двох копій одного додатку

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

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

Характеристики

Тип файла
Документ
Размер
695,5 Kb
Тип материала
Учебное заведение
Неизвестно

Список файлов реферата

Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6525
Авторов
на СтудИзбе
301
Средний доход
с одного платного файла
Обучение Подробнее