Personalcam.ru

Авто Аксессуары
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Монитор (синхронизация)

Монитор (синхронизация)

Монитор — в языках программирования высокоуровневый механизм взаимодействия и синхронизации процессов, обеспечивающий доступ к неразделяемым ресурсам. [1] Подход к синхронизации двух или более компьютерных задач, использующих общий ресурс, обычно аппаратуру или набор переменных.

При многозадачности, основанной на мониторах, компилятор или интерпретатор прозрачно для программиста вставляет код блокировки-разблокировки в оформленные соответствующим образом процедуры, избавляя программиста от явного обращения к примитивам синхронизации.

Содержание

История [ править | править код ]

Пер Бринч Хансен был первым, кто описал и реализовал мониторы, основывая их на идеях Хоара. Впоследствии Хоар разработал теоретическую основу и показал её эквивалентность семафорам (используя исходную семантику). Впервые воплощён в языке Concurrent Pascal и использован для структурирования межпроцессного взаимодействия в операционной системе Solo.

Взаимоисключительность [ править | править код ]

Монитор состоит из:

  • набора процедур, взаимодействующих с общим ресурсом
  • переменных, связанных с этим ресурсом , который определяет условия, позволяющие избежать состояние гонки

Процедура монитора захватывает мьютекс перед началом работы и держит его или до выхода из процедуры, или до момента ожидания условия (см. ниже). Если каждая процедура гарантирует, что перед освобождением мьютекса инвариант истинен, то никакая задача не может получить ресурс в состоянии, ведущем к гонке.

Простой пример. Рассмотрим монитор, выполняющий транзакции банковского счёта.

Инвариант здесь просто утверждает, что баланс должен отразить все прошедшие операции до того, как начнётся новая операция. Обычно это не выражено в коде, но подразумевается и может быть упомянуто в комментариях. Однако, есть языки программирования, такие как Эйфель или D, которые могут проверять инварианты. Блокировка добавлена компилятором. Это делает мониторы безопаснее и удобнее, чем другие подходы, требующие от программиста вручную добавлять операции блокировки-разблокировки, — поскольку программист может забыть добавить их.

Условные переменные [ править | править код ]

Чтобы избегать состояния активного ожидания, процессы должны сигнализировать друг другу об ожидаемых событиях. Мониторы обеспечивают эту возможность с помощью условных переменных. Когда процедура монитора требует для дальнейшей работы выполнения определённого условия, она ждёт связанную с ним условную переменную. Во время ожидания она временно отпускает мьютекс и выбывает из списка исполняющихся процессов. Любой процесс, который в дальнейшем приводит к выполнению этого условия, использует условную переменную для оповещения ждущего её процесса. Оповещённый процесс захватывает мьютекс обратно и может продолжать.

Следующий монитор использует условные переменные для реализации канала между процессами, который может хранить одномоментно только одно целочисленное значение.

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

Семантика Хоара и Mesa [ править | править код ]

В ранних реализациях монитора (известных как семантика Хоара) оповещение условной переменной немедленно активизирует ждущий процесс и восстанавливает блокировку, тем самым гарантируется, что условие всё ещё истинно.

Реализация этого поведения сложна и очень избыточна. Также она не совместима с вытесняющей многозадачностью, когда процесс может быть прерван в произвольный момент. По этим причинам исследователи разработали множество иных семантик для условных переменных.

В самых современных реализациях (известных как семантика Mesa) оповещение не прерывает работающий процесс, а просто переводит некоторые ждущие процессы в состояние готовности. Оповещающий процесс продолжает держать блокировку до тех пор, пока не выйдет из процедуры монитора. Побочные эффекты этого подхода в том, что оповещающий процесс не обязан соблюсти инвариант перед оповещением, а ожидающий процесс — должен повторно проверить условие, которого он дожидается. В частности, если процедура монитора включает выражение if test then wait(cv) , другой процесс может войти в монитор после момента оповещения и изменить значение test до того, как ждущий процесс возобновит работу. Выражение нужно переписать так: while test do wait(cv) , чтобы условие было пере-проверено после ожидания.

Реализации также предоставляют операцию «notifyAll», или «broadcast», которая оповещает все процессы, ждущие данное условие. Эта операция полезна, например, когда несколько процессов ждут доступности различных объёмов памяти. Освобождение памяти позволит продолжить работу кого-то из них, но планировщик не может знать, кого именно.

Delphi 7 потоки синхронизация переменных

При использовании в приложении нескольких потоков необходимо гарантировать, что в данный момент только один из потоков может иметь доступ к свойствам и методам объекта VCL — визуального компонента Delphi, то есть действия потоков необходимо синхронизировать между собой. Для выполнения такой синхронизации в Delphi применяется специальный метод Synchronize, в рамках которого и нужно вызывать процедуры, модифицирующие свойства визуальных компонентов.

Читайте так же:
Как отрегулировать температуру в твердотопливном котле

Процедура Synchronize использует в качестве параметра те процедуры, в которых происходит модификация свойств визуальных компонентов, и блокирует одновременный доступ к компоненту нескольких потоков. Вот какой пример, в частности, содержится в модуле, сгенерированном Мастером создания потока:

<Важно: Методы и свойства объектов в визуальных компонентах могут вызываться
только в методе Synchronize, например:>

procedure MyThread.UpdateCaption;
begin
Form1.Caption := ‘Updated in a thread’;
end;

procedure MyThread.Execute;
begin
Synchronize(UpdateCaption);
end;

В данном случае поток используется для изменения заголовка Формы. Изменение заголовка происходит в процедуре UpdateCaption. Казалось бы, для изменения заголовка эту процедуру достаточно вызвать в основной процедуре потока, Execute. Однако, если несколько таких потоков в программе одновременно попытаются изменить заголовок Формы, то это может привести к непредсказуемым последствиям. Для исключения этого процедура UpdateCaption вызывается в процедуре Execute как параметр метода Synchronize.

  • во-первых, частый вызов Synchronize тормозит выполнение приложения;
  • во-вторых, если практически все процедуры выполняющегося потока выполняются с использованием метода Synchronize, то смысла в создании такого потока нет — всё равно его работа пройдёт в главном потоке.

procedure TMyThread.UpdateCaption;
begin
while True do
begin
Form1.Caption:=IntToStr(I); //I — глобальная переменная основной программы, индекс массива
sleep(100);
end;
end;

procedure TMyThread.Execute;
begin
Synchronize(UpdateCaption);
end;

Видим, что происходит именно то, о чём написано выше. Так как весь код потока, и модификация заголовка Формы, и цикл ожидания, выполняется в методе Synchronize, а значит в главном потоке, то приложение будет выглядеть зависшим, и его даже будет невозможно корректно завершить.
Теперь попробуем вывести цикл за пределы Synchronize:

procedure TMyThread.UpdateCaption;
begin
Form1.Caption:=IntToStr(Cap);
end;

procedure TMyThread.Execute;
begin
while True do
begin
Synchronize(UpdateCaption);
sleep(100);
end;
end;

Это правильный вариант. С помощью метода Synchronize выполняется только непосредственная модификация Заголовка Формы, а цикл ожидания выполняется в потоке, и не мешает главному потоку.

Delphi.TThread. Правильно ли я делаю?

В программе из основного потока создаю дополнительный, который выполняет некоторую работу, пока эта работе не кончится, или пока основной поток не решит, что хватит уже эту работу делать. Управление работой второго потока делаю через булевскую переменную (ThreadWork). Хватит работать — в False ее. В True ставится только перед запуском потока. После установки в False второй поток всегда удаляется (ожидание завершения производиться автоматически в деструкторе, как я понял).

Теперь суть вопроса. Все разделяемые между потоками ресурсы предписывается синхронизировать. Насколько принципиально это правило? Является ли ошибкой то, что я обращаюсь к ThreadWork из обоих потоков без синхронизации? И какой отрицательный результат я могу получить? Замечу, что пока никаких проблем не возникало, но опыт подсказывает, что тест на своей тачке не говорит о 100%-ном результате. Привожу основу кода:

P.S.: Прошу еще учитывать вариант, когда ThreadWork:=False может произойти в цикле второго потока.

Да обычно ж такое ивентами делается.
Хатя вот инетерсно
ThreadWork:=False;
атомарная операция, в критическую секцию вроди заключать не требуется.

Wolfaka
>атомарная операция, в критическую секцию вроди заключать не требуется
Вот именно. Че тут делить-то?

>Да обычно ж такое ивентами делается
Эээ, это который WaitForSingleObject или типа того?

А TThread.Resume, Suspend, Terminate не катит? Вроде (не юзал TThread) там есть Terminated свойство.

DragonMX
ага да вот код выглядит както не так, с виду просто и хорошо, но я почемуто таким образом некогда не прерывал потоки 🙂

DragonMX
Обычно делается

И Sleep(55) это избыток, достаточно Sleep(10).

DeadMeat
>А TThread.Resume, Suspend, Terminate не катит?
Да, есть такие вещи, но со своими фичами. После TThread.Suspend поток нельзя просто уничтожить — он виснет на деструкторе. Надо завершить процедуру Execute, а потом его удалить. Принудительно грохнуть поток Terminate’ом у меня тоже не получилось. Если посмотреть сырцы, то там ставится некий флажок, мол, надо поток остановить, но потом вызывается WaitFor. Я не знаю, когда и как это должно сработать, но в моем случае, когда во втором потоке создается OLE-объект и останавливается на одной из его функций, поток не удаляется, пока его держит этот OLE-объект.

Читайте так же:
Как правильно отрегулировать смесь на карбюраторе пирбург 2е

Да и вообще, жестко убивать поток не хотелось бы, так как после цикла надо бы завершить работу с OLE-объектом и освободить его ресурсы. Из основного потока я этого сделать не могу, так как идет привязка к потоку.

Wolfaka
>но я почемуто таким образом некогда не прерывал потоки
Если не составит труда, покажи свой способ.

Мерлин
>while not(Terminated) do .
Хм, а почему бы и нет. Спасибо. Не надо объявлять переменную 🙂 Но тогда получается тоже самое — одна и та же переменная доступна из двух потоков без синхронизации.

>И Sleep(55) это избыток, достаточно Sleep(10).
Минуточку, а ведь Sleep выполняется на том самом таймере, дискретность которого 55 мсек. Или я заблуждаюсь?

>Но тогда получается тоже самое — одна и та же переменная доступна из двух потоков без синхронизации.
Не все на свете нужно синхронизировать. Тебе очень принципиально — завершится ли поток тут же или при следующем сравнении в while?

Ghost2
>Тебе очень принципиально — завершится ли поток тут же или при следующем сравнении в while?
Как раз нет. Мне лишь надо гарантировать, что не вылетит какой-нибудь exception из-за совместного доступа.

DeadMeat
Посмотрел про Terminate в делфёвском хелпе — действительно, только устанавливает FTerminated в True и надеется на разумность кода потока 🙂 В общем, для нормального завершения в моей ситуации — то, что и нужно. Спасибо.

А про жесткое убиение потока — стандартных средств самого TThread я не нашел. Но вод этот код работает (про его "качество" ничего сказать не могу):

Естественно, завершающая секция потока не сбудется.

>вылетит какой-нибудь exception из-за совместного доступа.
Для это есть какие-то предпосылки?

Ghost2
>Для это есть какие-то предпосылки?
Логически — нет. Просто, хотел убедиться на опыте других.

Всем
В итоге, никто не сказал, что не следует использовать булевскую переменную между двумя потоками без синхронизации, значит буду спокойно ее юзать. Спасибо Мерлин, буду использовать поле самого TThread. Всем спасибо за участие.

DragonMX
>>И Sleep(55) это избыток, достаточно Sleep(10).
>Минуточку, а ведь Sleep выполняется на том самом таймере, дискретность которого
>55 мсек. Или я заблуждаюсь?
55мс — это для 9х систем, а для NT-систем дискретность — 10мс. При желании, для NT, можно задать и 1мс, но лучше этого не делать.
И не в таймере тут дело, а в планировщике задачь, переключающем потоки с такой дискретностью.
Кстати, подобного же эффекта можно добиться и вообще вызвав Sleep(0) или Sleep(1), дискретность переключения потоков тут роли играть не будет. Но это уже тонкости. На "мастерах делфи" рекомендовали как-то задавать Sleep(25). В интернете по этому поводу много статей написано.

>В итоге, никто не сказал, что не следует использовать булевскую переменную
>между двумя потоками без синхронизации, значит буду спокойно ее юзать.
Читать можно и без синхронизации, а писать — только с синхронизацией, и от типа переменной это не зависит.

Мерлин
>И не в таймере тут дело, а в планировщике задачь, переключающем потоки с такой дискретностью
Ох, разворошил яму, блин. Почитал, аж диву дался. Я че-то зациклился, что в Delphi TTimer работает с точностью не выше 55 миллисекунд, а тут оказывается другой способ обработки. Не, я знал, что есть в mmSystem точный таймер — сам им пользуюсь для вывода графики, но вот про Sleep думал, что от тоже того. 55 мсек 🙂 Порылся по инету, подтвердил твои слова. Еще раз тебе спасибо 🙂

>На "мастерах делфи" рекомендовали как-то задавать Sleep(25)
Ты про этот финт ушами?
0 + (55 mod 10) * (55 div 10)

Читайте так же:
Регулировка клапанов американского двигателя для мотоблока

Смысла не понял. А если серьезно, можно ли всегда использовать Sleep(0)? Там же было написано:
>Отдать остаток слайса равноприоритетному потоку.
Где можно подтвердить эту информацию? Иначе, начнешь так использовать, а потом на каком-нибудь проце цикл забъет все процессорное время. И так уже обжегся с одноядерками со своими потоками.

Sleep(0) это просто возвращение управления шедулеру. Если в очереди нет более приоритетных или ждущих потоков, то управление возвращается вызываемому потоку. Т.е. в нормальных условиях код

не многим отличается от просто

Про все эти тонкости написано две отличные книги — авторы Рихтер и Руссинович. Перед тем, как садиться за потоки неплохо было бы их прочитать.

Delphi 7 потоки синхронизация переменных


SergP ©   ( 2005-12-11 20:09 ) [0]

Мне нужно изменить значение переменной ShortDateFormat, затем выполнить некоторые действия, после чего восстановить значение этой переменной.

SaveSDF:=ShortDateFormat;
ShortDateFormat:=. ;
// Некоторые действия
ShortDateFormat:=SaveSDF;

Проблема в том что в приложении работает несколько потоков в которых, также как и в основном потоке используется данная конструкция. И поэтому в процессе выполнения данного кода в одном потоке не должны выполняться другие потоки.
Пока проблем не возникало. Но есть предчуствия, что это нехорошо. Но хочу подстраховаться.

Как сделать то что мне нужно?


DrPass ©   ( 2005-12-11 20:49 ) [1]

Если потоки реализованы через TThread, проще всего поместить это в отдельный метод и вызывать его через Synchronize. Если потоки реализованы классически, через CreateThread, то тогда нужно синхронизировать через критические секции. В Delphi 7+ можно вообще обойтись без этого, там существует возможность "локального" изменения форматов с помощью TFormatSettings, не задействуя глобальные переменные.


SergP ©   ( 2005-12-11 21:12 ) [2]


> DrPass ©   (11.12.05 20:49) [1]

Вобщем насчет дополнительных потоков понятно. (Synchronize).
А как быть с основным потоком?


DrPass ©   ( 2005-12-11 21:32 ) [3]

А с ним ничего и не надо — Synchronize выполняет метод в его контексте, так что он гарантированно будет синхронизирован.


Alexander Panov ©   ( 2005-12-11 21:39 ) [4]

DrPass ©   (11.12.05 21:32) [3]
А с ним ничего и не надо — Synchronize выполняет метод в его контексте, так что он гарантированно будет синхронизирован.

Это только в одном случае — если используется синхронизация методом TThread.Synchronize.
В остальных случаях сделать синхронизацию не удастся, так как эту переменную могут использовать и многочисленные компоненты, объекты, а также функции, спрятанные в глубинах VCL.

>SergP ©   (11.12.05 20:09)

Может быть, стоит пересмотреть логику приложения?

У меня в дополнительных потоках закачивается контент по http и https с сервера (через WinInet)  и сразу же происходит их парсинг (а там в контенте встречаются даты в разных форматах). А в основном потоке нужно делать TdateTime —> string в требуемом формате.
Но в принципе я понял: я могу обрабатывать полученый контент в основном потоке, и тогда проблемы исчезнут.


DrPass ©   ( 2005-12-11 22:52 ) [6]


> Это только в одном случае — если используется синхронизация
> методом TThread.Synchronize

Так он же так и делает


Alexander Panov ©   ( 2005-12-11 23:17 ) [7]

>SergP © (11.12.05 22:05) [5]
Я бы не стал закладываться на то, что в дополнительных потоках не используются глобальные переменные и, во избежание проблем, конвертацию даты проводил вруную, без использования функций, использующих эти переменные.


evvcom ©   ( 2005-12-12 10:04 ) [8]


> SergP ©

Тебе ж сказали про TFormatSettings, а ты проигнорировал. И не нужны никакие переприсваивания глобальных переменных и синхронизация потоков, которая вызовет довольно ресурсоемкое дополнительное ненужное переключение потоков.


Alexander Panov ©   ( 2005-12-12 10:46 ) [9]

evvcom ©   (12.12.05 10:04) [8]

Что такое TFormatSettings и где про него почитать?

Ввести критическую секцию, обращаться к ней во всех потоках перед выполнением указанного кода. Классика.

Читайте так же:
Регулировка сцепления угб 50


Leonid Troyanovsky ©   ( 2005-12-12 13:47 ) [13]


> Игорь Шевченко ©   (12.12.05 13:05) [12]

> Ввести критическую секцию, обращаться к ней во всех потоках
> перед выполнением указанного кода. Классика.

Лучше, все же Synchronize. Бо, куда (и как) совать секцию в
первичном потоке — это еще искать надо.


Regards, LVT.


evvcom ©   ( 2005-12-12 14:49 ) [14]


> Alexander Panov ©   (12.12.05 10:46) [9]
> evvcom ©   (12.12.05 10:04) [8]
>
> Что такое TFormatSettings и где про него почитать?

В хелпе вестимо. Единственно на что я не обратил внимания — это на версию дельфи автора. Да, в Д7 есть, а в Д6 вроде как нет.


Alexander Panov ©   ( 2005-12-12 18:33 ) [15]

Leonid Troyanovsky ©   (12.12.05 13:47) [13]
Лучше, все же Synchronize. Бо, куда (и как) совать секцию в
первичном потоке — это еще искать надо.

Сдается мне, что ни один вариант не поможет, кроме как конвертация вручную, так как обращение к глобальным переменным выполняется во многих компонентах и функциях внутри VCL. Каждую функцию не защитишь, только если всю процедуру потока поместить в  Synchronize.


Leonid Troyanovsky ©   ( 2005-12-12 18:44 ) [16]


> Alexander Panov ©   (12.12.05 18:33) [15]

> Каждую функцию  не защитишь, только если всю процедуру потока
> поместить  в  Synchronize.

Всю — может быть излишне, а вот обращение к глобальным переменным
(заведомо принадлежащих первичному потоку) — всенепременно.
Т.е., само обращение достаточно универсально: сохраняем исходные,
делаем преобразование, восстанавливаем в finallly.

Думаю, что если алгоритм в потоке непрозрачный, т.е. используются дополнительные компоненты, например, из пакета Indy, то нельзя быть уверенным, что в этих компонентах нет обращения к глобальным переменным там, где нет защиты от одновременного чтения/записи. В этом случае получаем практически неуловимые ошибки при преобразовании дат в разных потоках.
Я это имею ввиду.

Организация многопоточной работы в Delphi. Поняте многопоточного приложения. Создание потоков. Потоки в Delphi выполняют функцию имитации псевдопараллельной. — презентация

Презентация на тему: » Организация многопоточной работы в Delphi. Поняте многопоточного приложения. Создание потоков. Потоки в Delphi выполняют функцию имитации псевдопараллельной.» — Транскрипт:

1 Организация многопоточной работы в Delphi

2 Поняте многопоточного приложения. Создание потоков. Потоки в Delphi выполняют функцию имитации псевдопараллельной работы приложения. Приложение Delphi, умеющее создать несколько потоков, получит больше времени операционной системы, и соответственно сможет выполнить больший объём работы. Создать дополнительный поток в Delphi поможет объект TThread. Ввести объект TThread в программу можно двумя способами: 1. с помощью Мастера; 2. вручную. Мастер создания дополнительного потока в Delphi создаёт отдельный модуль, в рамках которого выполняется поток. File -> New -> Other. В появившейся табличке выбора найдём TThread Object. Появится окошко, в верхнюю строку которого (Class Name) введём имя нашего будущего потока: MyThread.

3 Создание потока с помощью мастера В результате будет создан модуль, содержащий заготовку кода, реализующего дополнительный поток Delphi. unit Unit2; // Имя модуля, содержащего поток. При сохранении его можно изменить. interface uses Classes; type MyThread = class(TThread) //MyThread — заданное нами имя потока. private < Private declarations >protected procedure Execute; override; end; implementation < Important: Methods and properties of objects in visual components can only be used in a method called using Synchronize> < MyThread >procedure MyThread.Execute; begin < Place thread code here >end; end.

4 Создание потока вручную В первом способе класс MyThread был создан мастером в дополнительном модуле. Второй способ состоит в том, что можно создать такой класс в рамках одного из уже существующих модулей программы, например, в модуле Unit1: unit Unit1; //Обычный модуль в котором описывается основная программа … type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); end; //Здесь необходимо описать класс TMyThread: TMyThread = class(TThread) private < Private declarations >protected procedure Execute; override; end;

Читайте так же:
Регулировка оборотов на c20ne

5 Создание потока вручную var Form1: TForm1; //Нужно ввести переменную класса TMyThread MyThread: TMyThread; implementation <$R *.dfm>//Нужно создать процедуру Execute, уже описанную в классе TMyThread procedure TMyThread.Execute; begin //Здесь описывается код, который будет выполняться в потоке end; Если поток создаётся мастером, т.е. в другом модуле, то не забудьте в основном модуле описать переменную — экземпляр потока. Также, поскольку класс потока описан в другом модуле, имя этого модуля необходимо добавить в секцию uses.uses

6 Создание потока вручную Теперь можно запускать поток, даже если в его процедуре Execute нет ни единого оператора. //Запускать поток будем нажатием на кнопку: procedure TForm1.Button1Click(Sender: TObject); begin //Вначале нужно создать экземпляр потока: MyThread:=TMyThread.Create(False); //Параметр False запускает поток сразу после создания, True — запуск впоследствии, методом Resume //Далее можно указать параметры потока, например приоритет: MyThread.Priority:=tpNormal; end; Если в основной программе попробовать выполнить такой цикл: while True do; то приложение зависнет. А если поместить его в процедуру Execute. При нажатии на кнопку наш бесконечный цикл будет непрерывно выполняться в потоке, однако и приложение как целое не зависнет.

7 Завершение работы потока Поток обладает возможностями, позволяющими из основной программы передать ему приказ прекратить работу. Метод потока Terminate устанавливает свойство Terminated потока в True. Анализируя это свойство, поток может понять, что он должен завершить работу. procedure TMyThread.Execute; begin while True do if MyThread.Teminated then break; end; Этот код выполняет бесконечный цикл. Однако, при выполнении в основной программе оператора: MyThread.Terminate; цикл завершается, и поток прекращает свою работу.

8 Приоритеты потоков При работе с потоками необходимо учитывать приоритет создаваемых потоков. Так, если в предыдущем примере запустить не один поток, а два или больше, нажав на кнопку несколько раз, то компьютер станет очень заметно «тормозить». Это происходит потому, что приоритет по умолчанию новых потоков — нормальный. Можно уменьшить его, задав MyThread.Priority:=tpLower ; Этого достаточно, чтобы компьютер чувствовал себя более свободно. ПриоритетОписание tpIdle Низший приоритет. Поток получает время только тогда, когда операционая система находится в состоянии простоя. tpLowestПриоритет на два пункта ниже нормального tpLowerПриоритет на один пункт ниже нормального tpNormalНормальный приоритет tpHigherПриоритет на один пункт выше нормального tpHighestПриоритет на два пункта выше нормального tpTimeCritical Максимальный приоритет. Приоритет на уровне функций ядра операционной системы.

9 Синхронизация потоков При использовании в приложении нескольких потоков необходимо гарантировать, что в данный момент только один из потоков может иметь доступ к свойствам и методам объекта VCL — визуального компонента Delphi. Для выполнения такой синхронизации в Delphi применяется специальный метод Synchronize, в рамках которого и нужно вызывать процедуры, модифицирующие свойства визуальных компонентов. Процедура Synchronize использует в качестве параметра те процедуры, в которых происходит модификация свойств визуальных компонентов, и блокирует одновременный доступ к компоненту нескольких потоков. <Важно: Методы и свойства объектов в визуальных компонентах могут вызываться только в методе Synchronize, например:>procedure MyThread.UpdateCaption; begin Form1.Caption := ‘Updated in a thread’; end; procedure MyThread.Execute; begin Synchronize(UpdateCaption); end; В данном случае поток используется для изменения заголовка Формы. Изменение заголовка происходит в процедуре UpdateCaption.

10 Синхронизация потоков Метод Synchronize выполняется в главном потоке приложения. Поэтому, работая с несколькими потоками в приложении и применяя метод Synchronize, нужно учитывать, что: во-первых, частый вызов Synchronize тормозит выполнение приложения; во-вторых, если практически все процедуры выполняющегося потока выполняются с использованием метода Synchronize, то смысла в создании такого потока нет — всё равно его работа пройдёт в главном потоке. procedure TMyThread.UpdateCaption; begin Form1.Caption:=IntToStr(Cap); //Cap – глобальная переменная end; procedure TMyThread.Execute; begin while True do begin Cap:=Cap+1; Synchronize(UpdateCaption); sleep(100); end; end;

11 Многопоточные приложения Разработать приложение, которое генерирует 2 массива случайных чисел (по элементов). В отдельных потоков реализовать вычисление массива суммы и массива произведения из двух исходных. TMyThread = class(TThread) //новый класс потока private oper: char; //перемення для получения операции из внешней protecte // программы procedure ShowMas1; //процедура вывода элемнта массива mas1 procedure ShowMas2; //процедура вывода элемнта массива mas2 procedure Execute; override; end;

голоса
Рейтинг статьи
Ссылка на основную публикацию