Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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
3-1115573104
anton_m
2005-05-08 21:25
2005.06.14
вопрос по SQL


1-1117568825
Demonix
2005-05-31 23:47
2005.06.14
объект Edit1 и командная строка


4-1114001856
Medved
2005-04-20 16:57
2005.06.14
Отслеживание запуска программ.


14-1116690237
Nikolay M.
2005-05-21 19:43
2005.06.14
Кто помнит, как добраться до места прошлогодней MMP?


14-1116677046
Иксик
2005-05-21 16:04
2005.06.14
Крупный митинг





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский