Форум: "Основная";
Текущий архив: 2005.06.14;
Скачать: [xml.tar.bz2];
ВнизКак сделать чтобы форма реагировала во время работы потоков. Найти похожие ветки
← →
Kolan © (2005-05-27 10:09) [0]Здравствуйте,
Разбираюсь с потоками, нашел статью:
http://www.realcoding.net/article/view/158
Сделал пример где два потока, один включает флажок CheckBox"а другой выключает.
Если я всё правильно понял, то потоки должны работать себе и работать а форма нормально реагировать на все события.
Но форма как будто висит. Точнее мне всёже удалось передвинуть её, но переместилась она с большой задержкой.
Вот код потока:type
TMyThread1 = class(TThread)
private
{ Private declarations }
protected
procedure DoWork;
procedure Execute; override;
end;procedure TMyThread1.Execute;
begin
while not Terminated do
Synchronize(DoWork);
end;
procedure TMyThread1.DoWork;
begin
Form1.CheckBox.Checked := True;
end;
Ну соотвественно второйForm1.CheckBox.Checked := False;
Код главного юнита.var
Form1: TForm1;
T1: TMyThread1;
T2: TMyThread2;
На форме одна кнопка:procedure TForm1.StopButtonClick(Sender: TObject);
begin
if StopButton.Caption = "Ñòîï" then
begin
{Ïðåðûâàåì îáà ïðîöåññà}
T1.Terminate;
T2.Terminate;
StopButton.Caption := "Ñòàðò";
end
else
begin
{Ñîçäàåì è ñðàçó çàïóñêàåì äâà ïðîöåññà}
T1 := TMyThread1.Create(False);
T2 := TMyThread2.Create(False);
StopButton.Caption := "Ñòîï";
end;
end;
Что я нетак понял или сделал?
← →
-=XP=- © (2005-05-27 10:13) [1]while not Terminated do
begin
Synchronize(DoWork);
Sleep(100); // милисекунд
end;
← →
ЮЮ © (2005-05-27 10:15) [2]Synchronize(DoWork);
Т.е. вся работа потоков сводится к выполнению методов DoWork в основном кодовом потоке, вместо того, чтобы делать нечто в своем потоке
← →
Kolan © (2005-05-27 10:16) [3]Гы я почти так и пытался, но сделал так:
procedure TMyThread1.DoWork;
begin
Form1.CheckBox.Checked := True;
Sleep(100);
end;
← →
Bel © (2005-05-27 10:17) [4]Естественно, форма будет тормозить, ведь Syncronize работает в контексте основного потока. Обычно в параллельный поток выносят действия, не связанные с интерфейсом, тогда главная форма тормозить не будет.
← →
Kolan © (2005-05-27 10:20) [5]
> ЮЮ © (27.05.05 10:15) [2]
Не понял. Просто этот пример работы с потоками неудачный?<quote из источника>
А теперь - замечательный пример для изучения Thread! В нижеследующем примере приложение создает два параллельно работающих процесса. Один Thread пытается установить флажок CheckBox1 в положение "включено" (Checked := True), а другой, передергивая первого - в "выключено".
</quote из источника>
Где взять хороший
← →
Kolan © (2005-05-27 10:21) [6]
> Bel © (27.05.05 10:17) [4]
Простой пример можно?
← →
-=XP=- © (2005-05-27 10:26) [7]Простой пример можно?
Чем [1] не подходит?
← →
begin...end © (2005-05-27 10:37) [8]> Kolan © (27.05.05 10:20) [5]
> Где взять хороший
{Delphi}\Demos\Threads
← →
Kolan © (2005-05-27 10:38) [9]Всем подходит, я хочу понять просто как потоки использовать.
← →
Kolan © (2005-05-27 10:39) [10]
> begin...end © (27.05.05 10:37) [8]
Угу, посмотрю. :)
← →
-=XP=- © (2005-05-27 10:42) [11]В Вашем примере:
Следующий код выполняется в отдельном потоке:procedure TMyThread1.Execute;
begin
while not Terminated do
Synchronize(DoWork); // Вызывается метод Synchronize для синхронизации с основным потоком
end;
Весь следующий метод выполняется в основном потоке:procedure TMyThread1.DoWork;
begin
Form1.CheckBox.Checked := True;
end;
Поэтому, если Вы напишите Sleep() в методе DoWork, то тем самым "усыпите" основной поток. Насколько я понял, Вам не это нужно? Поэтому пишите Sleep() в том месте, где "усыпление" дополнительного потока не вызовет "усыпление" основного потока, то есть, вне методов, вызванных через Synchronize().
Итого:procedure TMyThread1.Execute;
begin
while not Terminated do
begin
Synchronize(DoWork);
Sleep(100); // Заснет только этот поток; основной поток продолжит выполнение.
end;
end;
procedure TMyThread1.DoWork;
begin
Form1.CheckBox.Checked := True;
end;
← →
Polevi © (2005-05-27 11:25) [12]>-=XP=- © (27.05.05 10:42) [11]
иногда лучше жевать, ей богу
← →
Игорь Шевченко © (2005-05-27 11:29) [13]Архангельский маст дай.
← →
-=XP=- © (2005-05-27 11:29) [14]Polevi © (27.05.05 11:25) [12]
Можно поподробнее?
← →
TUser © (2005-05-27 11:33) [15]> Polevi © (27.05.05 11:25) [12]
> Игорь Шевченко © (27.05.05 11:29) [13]
А что он сказал не так? Весь Execute не синхронизируется, синхронизируется только метод, который работает с VCL - как и должно быть.
Другой вопрос, что для такой цели отд. поток не нужен, но если все-таки потоком это делать, то я не понял, где в [11] ошибка.
← →
Alexander Panov © (2005-05-27 11:45) [16]Посмотри на такой пример.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, SyncObjs;
type
TForm1 = class(TForm)
CheckBox1: TCheckBox;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TTestThread=class(TThread)
private
FCS: TCriticalSection;
FCB: TCheckBox;
FInterval: Cardinal;
FChecked: Boolean;
procedure Lock;
procedure Unlock;
function GetInterval: Cardinal;
procedure SetInterval(const Value: Cardinal);
procedure _Sleep;
protected
procedure UpdateControl;
procedure Execute; override;
public
constructor Create(Control: TCheckBox; Interval: Cardinal; Checked: Boolean);
destructor Destroy; override;
property Interval: Cardinal read GetInterval write SetInterval;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TTestThread }
constructor TTestThread.Create(Control: TCheckBox; Interval: Cardinal;
Checked: Boolean);
begin
inherited Create(True);
Randomize;
FCS := TCriticalSection.Create;
FCB := Control;
FInterval := Interval;
FChecked := Checked;
FreeOnTerminate := True;
Resume;
end;
destructor TTestThread.Destroy;
begin
FCS.Free;
inherited;
end;
procedure TTestThread.Execute;
begin
while not Terminated do
begin
Synchronize(UpdateControl);
_Sleep;
end;
end;
function TTestThread.GetInterval: Cardinal;
begin
Lock;
try
Result := FInterval;
finally
Unlock;
end;
end;
procedure TTestThread.Lock;
begin
FCS.Enter;
end;
procedure TTestThread.SetInterval(const Value: Cardinal);
begin
Lock;
try
FInterval := Value;
finally
Unlock;
end;
end;
procedure TTestThread.Unlock;
begin
FCS.Leave;
end;
procedure TTestThread.UpdateControl;
begin
FCB.Checked := FChecked;
end;
procedure TTestThread._Sleep;
var
Times: Cardinal;
begin
if FInterval=0
then Times := 10+Random(200)
else Times := FInterval;
Sleep(Times);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
t1,t2: TTestThread;
begin
t1 := TTestThread.Create(CheckBox1,0,True);
t2 := TTestThread.Create(CheckBox1,0,False);
end;
end.
← →
-=XP=- © (2005-05-27 11:52) [17]Извините, может, я и не прав, но зачем в
procedure TTestThread.SetInterval(const Value: Cardinal);
function TTestThread.GetInterval: Cardinal;
используется критическая секция? Или это для примера?
← →
Alexander Panov © (2005-05-27 12:00) [18]-=XP=- © (27.05.05 11:52) [17]
используется критическая секция? Или это для примера?
Изменять значение FIinterval могут несколько потоков одновременно.
← →
-=XP=- © (2005-05-27 12:15) [19]Изменять значение FIinterval могут несколько потоков одновременно.
Это понятно. Просто FInterval имеет тип Cardinal. Насколько я понимаю, защита читаемой/записываемой памяти реализована на уровне ОС, если не процессора(ов). То-есть, в любом случае будет записано/прочитано верное значение. В смысле, потоки не будут, в случае возникновения конкурентной ситуации, портить (именно портить) данные. То-есть, если в один момент времени два потока решат писать в эту переменную данные, то после того, как они оба завершат сие действие, в переменной останется последнее записанное значение (не важно кем). Мусор там, по идее не появится. Под "мусором" я имею в виду: писали из одного потока 10, из другого 20, а получили в переменной 18.
Или, все же, такая ситуация может произойти?
Я так думаю, критическую секцию надо использовать только тогда, когда необходимо выполнить несколько неразрывно связанных последовательных действий. Например, одновременно установить значения нескольких переменных. В таком случае критическая секция необходима для обеспечения непротиворечивости значений этих переменных. То-есть, когда между изменениями двух (логически) связанных переменных процесоор может переключится на выполнение другого потока. То-есть, некое подобие "транзакции" (прошу прощения за подобную аналогию, но мне кажется, здесь есть сходство).
Хотя, и в [16] она не принесет вреда, разве что некоторое небольшое сокращение производительности.
← →
Erik1 © (2005-05-27 12:16) [20]А вобщето господину "Карих Николай" - это автор статьи, надо яйца отрезать. За глубокое непонимание потоков и их синхронизации, а то, что он читает Архангельский, тоглько усигубляет его вину и неможет служить смягчающим обстоятельством.
Вывод: Неходи больше на этот сайт и не читай глупости.
← →
Kolan © (2005-05-27 12:18) [21]Благодарю за ответы.
На самом деле
Я хочу написать dll. Для работы с Com портом. С записью проблем нет. Вот чтение хочу сделать в потоке. Нашел статью
http://articles.org.ru/docum/comport.php
И решил для начала разабраться с потоками. Мне подсказали что можно использовать механизм сообшений. Однако если я хочу работать в режиме: "послал команду, получил ответ(ответ 2-3байта), обработал", то этот подход непройдет, так как медленно, и надо делать буфер итд.
Как можно решить эту проблемму. Хотелось бы именно работать в режиме послал принял. Скорость порта 115200. И именно dll. чтобы изменить если что.
← →
Игорь Шевченко © (2005-05-27 12:24) [22]
> Изменять значение FIinterval могут несколько потоков одновременно.
Это как ? Переменная же является приватной для каждого потока ?
← →
-=XP=- © (2005-05-27 12:28) [23]Это как ? Переменная же является приватной для каждого потока ?
Про помощи метода SetInterval().
Речь идет о необходимости критической секции внутри методов SetInterval/GetInterval.
← →
Kolan © (2005-05-27 12:32) [24]
> Erik1 © (27.05.05 12:16) [20]
Ну дык ктож знал. Чё первое попалось то и прочел. :).
← →
Игорь Шевченко © (2005-05-27 12:33) [25]-=XP=- © (27.05.05 12:28) [23]
> Про помощи метода SetInterval().
Угу, сообразил.
В таком случае, разумно и чтение в методе _Sleep защитить критической секцией ?
← →
-=XP=- © (2005-05-27 12:46) [26]В таком случае, разумно и чтение в методе _Sleep защитить критической секцией ?
Вот по этому поводу и вопрос: Нужно ли чтение/запись переменной типа Cardinal (32 бита) защищать критической секцией?
То-есть, может ли поток быть приостановлен (для передачи процессора в ведение другого потока) в ходе выполнения чтения/записи этой переменной? Или, если это многопроцессорная система, то может ли ячейка памяти читаться одним процессором в тот же момент, когда другой процессор производит в нее запись? Защищена ли память от таких одновременных операций (то-есть, один процессор пишет старший байт 32-битной переменной, а другой читает младший байт)? Является ли операция (сохранение, например, значения из регистра процессора в ячейку памяти) атомарной по сути своей?
← →
Игорь Шевченко © (2005-05-27 12:57) [27]
> То-есть, может ли поток быть приостановлен (для передачи
> процессора в ведение другого потока) в ходе выполнения чтения/записи
> этой переменной?
В ходе записи - не может. В ходе чтения, а конкретно в методе _Sleep - может, теоретически. То есть, критическая секция здесь ни к чему.
← →
TUser © (2005-05-27 12:59) [28]И все-таки что неправильно в [11]?
← →
Digitman © (2005-05-27 13:07) [29]
> Является ли операция (сохранение, например, значения из
> регистра процессора в ячейку памяти) атомарной по сути своей?
за ответом на вопрос заглянем в мануал Интела
смотрим, что Интел говорит о префиксе LOCK, который собственно и делает инструкцию атомарной
Causes the processor’s LOCK# signal to be asserted during execution of the accompanying
instruction (turns the instruction into an atomic instruction). In a multiprocessor environment,
the LOCK# signal insures that the processor has exclusive use of any shared memory while the
signal is asserted.
Note that in later IA-32 processors (including the Pentium 4, Intel Xeon, and P6 family proces-sors),
locking may occur without the LOCK# signal being asserted. See IA-32 Architecture
Compatibility below.
The LOCK prefix can be prepended only to the following instructions and only to those forms
of the instructions where the destination operand is a memory operand: ADD, ADC, AND,
BTC, BTR, BTS, CMPXCHG, CMPXCH8B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR,
XADD, and XCHG. If the LOCK prefix is used with one of these instructions and the source
operand is a memory operand, an undefined opcode exception (#UD) may be generated. An
undefined opcode exception will also be generated if the LOCK prefix is used with any instruc-tion
not in the above list. The XCHG instruction always asserts the LOCK# signal regardless of
the presence or absence of the LOCK prefix.
The LOCK prefix is typically used with the BTS instruction to perform a read-modify-write
operation on a memory location in shared memory environment.
The integrity of the LOCK prefix is not affected by the alignment of the memory field. Memory
locking is observed for arbitrarily misaligned fields.
мы видим, что MOV среди инструкций, к которым применим этот префикс, отсутствует, и это дает повод говорить об атомарности инструкции по дифолту
← →
-=XP=- © (2005-05-27 13:32) [30]2 Digitman
Спасибо
← →
Defunct © (2005-05-27 13:44) [31]Digitman © (27.05.05 13:07) [29]
> мы видим, что MOV среди инструкций, к которым применим этот префикс, отсутствует, и это дает повод говорить об атомарности инструкции по дифолту
Нет, такого повода, это нам не дает. В списке также отсутствуют строковые команда MOVS/STOS/LODS, которые впринципе не атомарны.
← →
Alex Konshin © (2005-05-27 13:45) [32]Alexander Panov © (27.05.05 11:45) [16]
Несколько важных замечаний по примеру:
1. Пока не буду вдаваться в обсуждение нужно ли защищать изменение FInterval. Просто хочу указать, что если уж нужно, то довольно часто можно обойтись вызовом одной из из функций Interlocked* (например, InterlockedExchange, InterlockedIncrement) или же коммандой в ассемблере с модификатором lock.
2.Применять критические секции, как и другие средства блокировки нужно обдуманно и обоснованно. В противном случае можешь иметь проблемы с deadlock. Особенно осторожно нужно быть в случае вложенных критических секции (с несколькими критическими секциями). В этом случае deadlock довольно просто получить, если в разных последовательностях исполнения вложение происходит в разном порядке. Вложенные критические секции могут получится, например, в результате прямого или косвенного вызова методов, которые используют другую критическую секцию. Еще обратите внимание, что под "критические секции" в этом пункте я подразумеваю не только TRtlCriticalSection и его производные, но и семафоры и т.п., т.е. все, что может блокировать исполнение.
3. Когда нужно применять критические секции или другие способы синхронизации доступа? Очень распространенная ошибка начинающих (и не только) в том, что они считают, что защиты требуют только глобальные переменные. Другое заблуждение мы только что видели: то, что не нужно защищать приватные переменные. Третье заблуждение мы тоже только что видели: то, что нужно защищать только тогда, когда изменяется более одной переменной. На самом же деле нужно защищать тогда и только тогда, когда:
- Существует возможность доступа к этому ресурсу несколькоми потоками (при этом абсолютно не играет роли, что этот ресурс - приватная переменная какого-то объекта, пусть даже наследника TThread).
- Операция с этим ресурсом неатомарная и изменение ресурса зависит от его текущего состояния/значения. Например, если вы читаете некую переменную, сравниваете ее с чем-нибудь и в зависимости от результата можете изменить ее значение, то эта операция неатомарная (кстати, ее часто можно заменить на одну из атомарных функций Interlocked*). Если вы даже просто прибавляете к некому полю некое число, то это тоже неатомарная операция (опять-таки, это можно сделать с помощью атомарной InterlockedIncrement).
← →
Alex Konshin © (2005-05-27 13:52) [33]О, пока писал, тут уже понаписали.
Digitman © (27.05.05 13:07) [29]
Да, mov атомарный, но нужно пояснить, что из этого не следует, что любое присваивание в Delphi - атомарно, потому значение в регистре должно еще как-то оказаться. Простой пример: два переменные и мы присваиваем значение одной переменной другой. Это - неатомарная операция. Вот присваивание константы - атомарная операция.
← →
Суслик © (2005-05-27 13:55) [34]Еще неатомарная операция: присваивание константы значению типа int64
← →
Германн © (2005-05-27 13:55) [35]2Игорь Шевченко © (27.05.05 11:29) [13]
>Архангельский маст дай.
А это не Архангельский! Это тот же Карих Николай, которого Digitman терпеть не может. Странно, что последний в данном случае промолчал. :)
← →
Defunct © (2005-05-27 13:56) [36]Alex Konshin © (27.05.05 13:52) [33]
> Да, mov атомарный, но нужно пояснить
А как же быть с Movs? Тоже атомарный?
Интересует что же происходит с сигналом Lock# при отработке строковых операций.
← →
Digitman © (2005-05-27 14:04) [37]
> В списке также отсутствуют строковые команда MOVS/STOS/LODS,
> которые впринципе не атомарны
речь не идет о строковых инструкциях, с ними - иная ситуация ... понятно что они изначально не могут быть атомарными ...
← →
Alex Konshin © (2005-05-27 14:04) [38]Цепочечные операции точно неатомарные, потому что они могут быть прерваны.
← →
Digitman © (2005-05-27 14:10) [39]
> Alex Konshin © (27.05.05 14:04) [38]
ну эт ты загнул)
прерваны они, конечно, м.б., но только не одиночные (т.е. если используется REPХХ-префикс)
← →
Alex Konshin © (2005-05-27 14:14) [40]Digitman © (27.05.05 14:10) [39]
> Alex Konshin © (27.05.05 14:04) [38]
ну эт ты загнул)
прерваны они, конечно, м.б., но только не одиночные (т.е. если используется REPХХ-префикс)
А я и сказал цепочечные операции. И опять-таки не забывай, что значения в регистрах должны как-то оказаться, т.е. одиночной командой ты не обойдешься. А они вместе уж всяко не будут атомарными.
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2005.06.14;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.039 c