Форум: "Основная";
Текущий архив: 2003.01.09;
Скачать: [xml.tar.bz2];
ВнизСоздание двух потоков Найти похожие ветки
← →
tiam (2002-12-24 14:49) [0]Добрый день.
Кто может привести краткий и понятный код, либо дать ссылку, как пример работы двух потоков, например, в одном идут вычисления и вывод на битмап, а в другом, в это же время ввод данных в едит.
Те примеры, что видел, не очень наглядны, слишком запутаны, как, например, этот: http://delphi.mastak.ru/articles/panov/index.html.
Стандартный демопример из Делфи тоже не слишком нагляден.
Спасибо.
← →
Digitman (2002-12-24 14:54) [1]
> не очень наглядны, слишком запутаны
> демопример из Делфи тоже не слишком нагляден
Что ненаглядно-непонятно ? Что запутано ? Конкретней, пожалуйста, со ссылкой на "непонятно-ненаглядно-запутанный" код.
← →
bak (2002-12-24 15:06) [2]у меня есть простейший пример использования 2х потоков - изменяют прогресс в гаудже. Прикрутить другие процедуры я думаю не составит труда. Хошь - намылю.
← →
tiam (2002-12-24 15:10) [3]Слишком много действий в коде (демопример Делфи), теряется сама тема за сортировой и массивами. Надо ли создавать отдельный юнит (new/thread object). А в примере http://delphi.mastak.ru/articles/panov/index.html не везде четко указано, что и где размещать, путаница с названиями юнитов, лучше бы просто исходники были примера.
Поэтому и прошу пример простейший. Два действия, выполняемых одновременно (опрос клавиатуры и вывод какой-либо), так как столкнулся с проблемой при большом количестве вычислений, программа не реагирует на нажатия клавиш, несмотря на всевозможные "process messages".
Итак,
Как разделить ввод и вывод на 2 потока, как потоки инициализировать (это вроде понятно) и выполнить в них простейший код?
Где еще статьи на эту тему для начинающих?
Спасибо.
← →
tiam (2002-12-24 15:16) [4]2 bak
Да, пожалуйста, на tiam@rambler.ru
← →
tiam (2002-12-24 15:29) [5]Так никто с ответом не поможет?
← →
sniknik (2002-12-24 15:39) [6]посмотри ссылку
http://gurin.tomsknet.ru/gala.html
там правда основа собственный компонент но лутшего описания потоков я не вилел, примеры есть и простые можно легко переделать на стандартные.
← →
Digitman (2002-12-24 15:39) [7]
> Надо ли создавать отдельный юнит (new/thread object)
Не обязательно. Класс потока можно декларировать и реализовать в любом удобном для тебя юните, в т.ч. и отдельном.
Меню "new/thread object" предназначено всего лишь для запуска эксперта, который (для твоего удобства лишь) заведет отдельный юнит в проекте, "набъет" там декларацию нового класса-наследника TThread и реализацию-"заготовку" метода Execute()
В теле "заготовки" делай длительные вычисления.
После того, как Execute() завершит свою работу, поток так же завершается.
> опрос клавиатуры и вывод какой-либо
На то есть уже один поток - основной. Это его задача - ввод/вывод - и он с этим успешно справляется, так что - никаких доп.забот по этому поводу
Забота у тебя одна - выполнить, например, в цикле от 1 до 99 999 999 что-то. Если это делать "в лоб", в одном из обработчиков событий той же формы, то все это будет выполняться в осн.потоке, при этом все прочие события (например, ввода/вывода) будут ждать своей очереди на обработку и форма как бы "зависнет" на время выполнения цикла, не реагируя ни на мышь ни на клаву и не рисуя ничего на своей канве (это же и есть обработка событий ввода/вывода !)
В подобном случае есть смысл "вынести" тело цикла в доп.поток, в тело процедуры Execute(). Будучи сконструированным тобой в нужный момент времени (TMyThread.Create(..)), новый объект-наследник TMyThread = class(TThread) даст команду ОС на запуск в твоем процессе нового доп.код.потока и известит тебя о фактическом начале его работы вызовом процедуры Execute(). Все, что ты предусмотрел в теле этой процедуры, будет выполняться параллельно с работой других код.потоков процесса, в т.ч. основного. Т.е. ввод/вывод в осн.потоке - сам по себе, а цикл в твоем доп.потоке - сам по себе, совершенно независимо друг от друга.
← →
sniknik (2002-12-24 15:42) [8]похоже я ошибался насчет описания. :о))
← →
tiam (2002-12-24 15:45) [9]2 digitman.
Спасибо...
← →
drin (2002-12-25 04:19) [10]Thread слишком сильно запутан, как по мне, я же использую API ф-ии, для работы с потоками, пример:
Var
hThr1, hThr2: THandle;
ThrID1, ThrID2: DWORD;
...
procedure My_Proc1;
begin
while true do
begin
ShowMessage("Proc 1");
sleep(10000);
end;
end;
procedure My_Proc2;
begin
while true do
begin
ShowMessage("Proc 2");
sleep(3000);
end;
end;
begin
hThr1:=CreateThread(nil, 0, @My_Proc1, nil, 0, ThrID1);
hThr2:=CreateThread(nil, 0, @My_Proc2, nil, 0, ThrID2);
end;
Всё что выполняется в ф-ях My_Proc1, My_Proc2 выполняется паралельно между собой и с тем что выполняется в основном потоке в том числе и прорисовка.
Тоесть каждые 10 сек будет выскакивать сообщение "Proc 1",
каждые 3 сек будет выскакивать сообщение "Proc 2" и при этом прорисовка и обновление главной формы не приостановятся.
← →
Alex Konshin (2002-12-25 05:47) [11]Еще раз: используйте BeginThread/EndThtread, а не CreateThread.
Для конкретно этого примера это может и не критично, но потом люди глядя на этот пример нарвутся на грабли.
← →
tiam (2002-12-25 11:17) [12]А как грамотно уничтожить поток после выполнения
← →
tiam (2002-12-25 11:53) [13]Допустим, поток должен остановиться в случае событий клавиатуры.
Почему выполнение потока приостанавливается самопроизвольно
← →
Digitman (2002-12-25 12:02) [14]
> поток должен остановиться
Что значит "остановиться" ? И зачем ему останавливаться, если он занимается обработкой данных ? Поясни
← →
Александр Спелицин (2002-12-25 12:10) [15]2Alex Konshin
> используйте BeginThread/EndThtread, а не CreateThread.
Простите, а где именно могут быть грабли при использовании CreateThread?
← →
tiam (2002-12-25 13:35) [16]2 Digitman
Делаю программу, которая должна закрываться при нажатии клавиши (по типу screensaver), так как расчет перед выводом на битмап занимает много времени, то его и пытаюсь загнать в отдельный процесс.
unit stream; // модуль потока
type
tmystream = class(TThread)
Создание потока в главном модуле:
MyProcess:=stream.TMystream.Create(true);
MyProcess.Priority:=tpLower;
MyProcess.Resume;
В самом потоке происходит подготовка битмапа и вывод.
Synchronize() я не делаю, так как не совсем понял с чем в моем случае нужно синхронизироваться
в главной форме:
if Msg.message = WM_KEYDOwn then
begin
application.Terminate;
MyProcess.Terminate;
end;
То есть, при нажатии клавиши вся программа прекращает работу, и поток останавливается.
Так ли это?
Поток останавливается в разных состояниях (выводит то весь текст, то часть его), хотя клавиши не нажимаю. В каких случаях поток может прекратить работу?
И еще вопрос:
Действительно ли удобнее использовать API ф-ии для работы с потоками?
Спасибо
← →
Polevi (2002-12-25 13:36) [17]Use this routine or a TThread object to spawn separate threads of execution instead of calling the CreateThread Win32API directly. BeginThread encapsulates the Win32 CreateThread API call, but unlike CreateThread, it sets the global IsMultiThread variable, thereby making the heap thread-safe.
IsMultiThread is set to True by BeginThread in order to allow the memory manager to execute safely if a VCL application has more than one thread. If a second thread attempts to allocate memory, it is blocked until the first thread exits the memory manager.
← →
Polevi (2002-12-25 13:39) [18]>tiam (25.12.02 13:35)
В самом потоке происходит подготовка битмапа и вывод.
Synchronize() я не делаю, так как не совсем понял с чем в моем случае нужно синхронизироваться
- синхронизироваться должно обращение к VCL
в главной форме:
if Msg.message = WM_KEYDOwn then
begin
MyProcess.WaitFor;
application.Terminate;
end;
← →
sel (2002-12-25 13:54) [19]2 POlevi
Application.Terminate уничтожает все потоки порожденные самим этим application?
Проверку нажатия клавиши куда лучше поставить в главном модуле?
По таймеру? И нужно ли в данном случае синхронизироваться?
а в самом потоке, в execute, писать таким образом?
procedure tmystream.Execute;
while not terminated do
begin
dosmth(form1.Memo1.Lines, form1.PaintBox1);
dosmthelse(form1.Memo1.Lines, form1.PaintBox1);
end;
end;
← →
Digitman (2002-12-25 14:18) [20]if Msg.message = WM_KEYDOwn then
begin
//сначала вызываем деструктор объекта, управляющего доп.потоком
MyThread.Free; // не путай понятия process и thread ! У тебя именно дополнительный thread уже работающего процесса
//и только затем даем команду процессу на завершение
Application.Terminate;
end;
Процесс не завершится до тех пор, пока не завершен хотя бы один из доп.потоков, созданных в ходе работы процесса.
Т.е., перед командой на завершение приложения (читай - процесса, в контексте которого приложение работает в дан.момент), ты обязан принять меры к корректному терминированию ВСЕХ созданных тобой доп.потоков
В ходе выполнения MyThread.Free перед разрушением объекта TMyThread будет вызван метод Terminate() базового класса (TThread).
Все, что делается в методе Terminate() - взводится некий внутренний флаг (поле TThread.FTerminated получает значение True)
Код работающего потока в теле метода Execute() имеет доступ к состоянию этого флага через св-во TThread.Terminated.
Выполняющий длит.вычисления поток должен КАК МИНИМУМ периодически контролировать состояние св-ва Terminated для того, чтобы "поймать" момент, когда от потока кто-то извне требует как можно быстрей "закруглиться". Как только алгоритм метода Execute() обнаружит условие Terminated = True, он ОБЯЗАН как можно быстрей завершить выполнение (процедура Execute должна быть как можно быстрей завершена)
← →
tiam (2002-12-25 14:32) [21]А что такое MyThread?
У меня поток создается так.
type
tmystream = class(TThread)
myProcess:=stream.TMystream.Create(true);
А если так написать: tmystream.Free, то ругается: "Этот метод только для методов классов"
← →
Digitman (2002-12-25 14:49) [22]
> А что такое MyThread?
а что такое MyProcess ???
и что такое stream ???
и вообще - ты взялся за потоки (действительно - не самое простое дело !), а в Object Pascal "плаваешь")
Отвлекись от своих потоков - вопрос тебе на засыпку :
если имеется класс TMyClass с конструктором Create(), то как создать экземпляр класса, получив при этом его адрес и зафиксировав в некоей переменной ?
← →
Digitman (2002-12-25 14:55) [23]к тому же понятия stream и thread (не смотря на то, что и то и другое достаточно правильно называть "поток") - совершенно разные ! И по сути и по назначению !
stream ("поток") = поток данных
thread ("нить", "кодовый поток") = поток маш.инструкций (или - маш.кода), выполняемых центральным процессором (одним или более) в многозадачной ОС
← →
Polevi (2002-12-25 14:55) [24]:-))
← →
tiam (2002-12-25 15:10) [25]Как я и создал:
tmystream = class(TThread);
myProcess:=TMystream.Create();
Вопрос в том, что такое MyThread
← →
Digitman (2002-12-25 15:26) [26]Ну при чем здесь stream и process ??? ну ведь имена идентификаторов в тексте программы должны же хоть как-то отражать их назначение и отношение к чему-либо ?
речь-то у тебя идет о кодовых потоках ! о thread"ах !
ну так и используй для этой цели "thread" ! а не "process" и не "stream" !! ... хоть "My" хоть не "My"))...
TMyThread = class(TThread) // декларация поточного класса
...
var
MyThread : TMyThread; // переменная, которая будет хранить адрес экз-ра поточного класса
...
MyThread := TMyThread.Create(...); // создаешь экземпляр поточного класса TMyThread и фиксируешь ссылку на него в соответствующей переменной MyThread
Что непонятно тут ?
← →
sel (2002-12-25 15:55) [27]Ну хорошо, напишу я так:
tmythread = class(TThread)
Secondthread:=TMythread.Create(true);
я так понял, что при нажатии любой клавиши в основном модуле я должен делать:
Secondthread.free;
application.terminate;
При этом на клавиши реакции нет.
Где я должен написать следующее и должен ли вообще:
Secondthread.FreeOnTerminate:=true;
Secondthread.Terminate;
Вывод на экран останавливается в разных состояниях при перезапуске программы, хотя должен пройти полностью.
← →
Digitman (2002-12-25 16:07) [28]
> Secondthread:=TMythread.Create(true);
Почему Create(true) ? Почему не Create(False) ? Где Resume в таком случае ?
> при нажатии любой клавиши в основном модуле я должен делать:
>
> Secondthread.free;
> application.terminate;
Да по барабану ! Хоть клавиша хоть не клавиша...
Суть одна и та же : если ты создал объект-поток вызовом конструктора, то обязан разрушить этот объект вызовом деструктора в ЛЮБОМ удобном месте программы и при ЛЮБОМ событии, но - ДО вызова "application.terminate"
Есть, правда, и другой, "скрытый" способ - заставить сам поток разрушить объект, создавший его. На то есть св-во FreeOnTerminate, установив которое (в любое время, пока поток еще работает) в True, ты тем самым указываешь потоку на необходимость самостоятельно вызвать деструктор своего управляющего объекта непосредственно перед завершением.
В таком случае для терминирования потока извне достаточно установить его св-во Terminated = True, все остальное поток сделает сам.
← →
tiam (2002-12-25 16:12) [29]я пишу:
Secondthread:=TMythread.Create(true);
Secondthread.Priority:=tpLower;
Secondthread.Resume;
просто указывал выше
← →
Digitman (2002-12-25 16:19) [30]Ну и ? А что у тебя делается в Execute() ?
← →
tiam (2002-12-25 16:21) [31]приложение не закрывается все равно, хотя поток должен был разрушиться
← →
Reindeer Moss Eater (2002-12-25 16:23) [32]SecondThread.Terminated := True;
Не останавливает поток. Всего лишь флаг Terminated устанавливается в True;
Внутри execute это надо проверять и выходить их метода.
← →
Digitman (2002-12-25 16:28) [33]а что за клоунада с переодеваниями ? то <team> то <sel>...
вроде бы об одном и том же речь идет ..
или я ошибаюсь ?)
← →
Digitman (2002-12-25 16:28) [34]а что за клоунада с переодеваниями ? то <tiam> то <sel>...
вроде бы об одном и том же речь идет ..
или я ошибаюсь ?)
← →
Polevi (2002-12-25 16:31) [35]не упрямся, дай код, мы не будем его использовать в корыстных целях
← →
tiam (2002-12-25 16:32) [36]Дело в том, что поток реагирует на движение мыши, даже если это не указано, то есть вывод на битмап останавливается, если пошевелить мышкой, например, а иногда и просто стопорится без видимых причин.
Как будто поток перестает выполняться.
А при повторном запуске может выполниться иначе(застрять в другом месте).
Как обрабатывается ввод, если не делаю синхронизацию?
Об этом я тоже спрашивал, никто не ответил.
← →
Digitman (2002-12-25 16:42) [37]Приведи код Execute() !
← →
tiam (2002-12-25 16:51) [38]в Execute следующее:
procedure tmythread.Execute;
begin
while (not Terminated) do // также пробовал и без этого условия
begin
RunText(form1.Memo1.Lines, form1.PaintBox1);
end;
end;
Сама процедура text - плывущий вверх текст, причем без строк:
while iterate do begin end; не выводит ничего на битмап.
procedure tmythread.Text(const SL: TStrings; const Pb: TPaintBox);
var i, w, y: integer;
Bmp, WorkBmp: TBitmap;
curpos, counter, maxcounter: integer;
cf: double;
function Iterate: boolean;
procedure PrepareBmp(const Source, Dest: TBitMap);
var x, y, mean, tmpx, tmpy: integer;
sb, db: PByteArray;
f, x1, y1, h, h0: double;
begin
h0 := Pb.Height;
h := Pb.Height;
f := 300;
result := counter<source.Height;
mean := (Source.Width - 1) div 2;
for y := 1 to Dest.Height - 1 do
begin
db := Dest.ScanLine[Dest.Height - y];
y1 := (y * counter + y * pb.height - h0 * pb.height - y + y * f - h0 * counter + h * f + h0 - h0 * f) / (-h0 + y);
tmpy := round(y1);
if (tmpy >= 0) and (tmpy < source.Height) then
begin
sb := Source.ScanLine[tmpy];
for x := 0 to Dest.Width - 1 do
begin
x1 := -(-y + h0 - h) * mean / (-h0 + y) - x * h / (-h0 + y);
tmpx := round(x1);
if (tmpx >= 0) and (tmpx < source.Width) then
begin
db[x * 2] := sb[2 * tmpx];
db[x * 2 + 1] := sb[2 * tmpx + 1];
end;
end;
end;
end;
end;
begin
inc(counter);
PatBlt(WorkBmp.Canvas.Handle, 0, 0, bmp.Width, bmp.Height, Blackness);
PrepareBmp(Bmp, WorkBmp);
Pb.Canvas.Draw(0, 0, workbmp);
dec(curpos);
end;
begin
counter := 0;
Bmp := TBitmap.Create;
Bmp.PixelFormat := pf16bit;
WorkBmp := TBitmap.Create;
with bmp do begin
width := Pb.Width;
Canvas.Font.Size := 18;
height := round(sl.Count * Canvas.Font.Size * 1.5) + 10 + pb.Height;
Canvas.Font.Name := "Arial";
Canvas.Font.Color := clwhite;
Canvas.Brush.Style := bsClear;
y := pb.Height;
PatBlt(Canvas.Handle, 0, 0, bmp.Width, bmp.Height, Blackness);
with bmp.canvas do
for i := 0 to sl.Count - 1 do
begin
w := TextWidth(sl[i]);
TextOut((bmp.Width - w) div 2, y, sl[i]);
y := y + (Font.Size * 3) div 2; ;
end;
curpos := Pb.Height - 2;
end;
WorkBmp.width := bmp.Width;
WorkBmp.Height := pb.Height;
WorkBmp.PixelFormat := pf16bit;
while iterate do
begin
end;
end;
← →
Polevi (2002-12-25 16:59) [39]надо формировать изображение в потоке, и посылать сообщение окну, в обработч которого рисовать его
← →
Digitman (2002-12-25 17:15) [40]выполнение строки
Pb.Canvas.Draw(0, 0, workbmp);
должно быть синхронизировано с осн.потоком
1. сделай workbmp не лок.переменной , а полем класса потока
2. помести указ.строчку в тело какой-нибудь метода типа TMyThread.DoDraw
3. вместо
Pb.Canvas.Draw(0, 0, workbmp);
пиши
Synchronize(DoDraw);
кр.того не вздумай менять StringList в осн.потоке, пока он передан на обработку дополнительному параметром Source.
лучше передавай копию StringList"а
или придется переделывать оч.многое в Execute() и RunText()
ввод, кстати, здесь совершенно ни при чем.
часть проблем у тебя именно из-за того, что один поток пытается что-то писать в StringList, а другой (не зная об изменениях) - читать из него.
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2003.01.09;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.011 c