Форум: "Основная";
Текущий архив: 2005.03.27;
Скачать: [xml.tar.bz2];
ВнизРаботаем с Thread-ми , очень нужно Найти похожие ветки
← →
ctranik (2005-03-07 14:58) [0]Всем привет
Требуется передавать данные во время работы ПОТОКА.
Предположим, что превоначально в этот ПОТОК загрузили текст
из компонента " Memo1 " (обычный текст).
Теперь содержимое компонента " Memo1 " изменилось(а поток-то работает!!! ).
Как новые данные загрузить в работающий ПОТОК ?
← →
Юрий Зотов © (2005-03-07 15:11) [1]Варианты:
1. Suspend - загрузка_данных - Resume.
2. Terminate - запуск_нового_потока_с_новыми_данными.
И еще много чего можно придумать. Надо знать детали задачи.
← →
DiamondShark © (2005-03-07 15:33) [2]Ни разу не понятно, что значит "загрузить данные в поток".
← →
Erik1 © (2005-03-07 16:58) [3]Наверное тебе необходима синхронизация с потоком для передачи и приема? Тогда глянь на критеческие секции и события. TCriticalSection TEvent.
← →
ctranik (2005-03-08 08:16) [4]
> Юрий Зотов
Привет
обьясни пожалуйсто, вот когда поток один раз выполнился , он чё после этого разрушается что ли ?
И ещё для использования "Terminate", в теле метода " Execute "
надо весь код поместить в конструкцию
while not Terminated do begin
.....//тут какй-то код
end;
а в событие OnClick кнопки написать следущие
mythread.create(true); //поток создан но невыполняется
...//тут какой-то код
...//тут какой-то код
mythread.resume; //запускаем
mythread.terminate; //а тут мы его разрушаем , правильно ???
ответь пожалуйсто на вопросы, а то честно говоря уже забодался
с этими потоками.
← →
Набережных С. © (2005-03-08 09:49) [5]Существуют две разные сущности - дельфийский объект TThread и системный объект THREAD. Дельфийский - самый обыкновенный объект, такой же, как и любой другой. Он при своем создании путем вызова WinApi функции CreateThread создает объект системный и связывает с ним некоторый код, частью которого является код процедуры TThread.Execute. Теперь, когда будет предоставлять процессорное время объекту THREAD, будет исполняться именно этот код.
Если ты создаешь дельфийский объект с параметром в конструкторе TRUE, функция CreateThread будет вызвана с установленным флагом CREATE_SUSPENDED. Это значит, что системный объект будет создан, с ним будет связан код, но исполняться этот код не будет. Поток будет "спать". После вызова метода Resume система будет считать, что поток разбужен и будет предоставлять ему возможность использовать процессор в порядке очередности с другими потоками.
Допустим, у нас есть несколько равноправных потоков, каждому из которых есть что делать, а процессор только один. С каждым потоком связан собственный код, который в общем случае никак не связан с кодом других потоков. Процессор не может одновременно выполнять две разные команды. Для того, чтобы каждый поток смог выполнить свою работу, система периодически "забирает" процессор у одного потока и отдает его другому. Т.е. некоторое время исполняется код только одного потока, остальные ждут своей очереди. На многопроцессорной машине происходит то же самое, принципиальных отличий нет, просто в каждый момент времени могут исполняться несколько потоков, по числу процессоров.
Смысл существования системного объекта THREAD - выполнение связанного с ним кода. Как только этот код завершился, функции объекта THREAD выполнены. Он еще продолжает существовать, пока где хранятся неосвобожденные ссылки на него, но вновь запустить исполнение связанног с ним кода он не может. Код исплняется только раз.
Дельфийский TThread как раз и удерживает одну из ссылок на THREAD. Это позволяет в одном месте хранить, например, результаты работы потока или получить его код завершения. Если этого нам не надо, то мы можем установить флаг FreeOnTerminate и тогда TThread сам себя уничтожит как только завершится, грубо говоря, метод Execute.
Так как поток может быть предназначен для циклической работы, не одноразовой, то он не должен до определенного момента завершать свое исполнение. Для этого в потоке организуется цикл. В этом случае в какой-то момент нужно извне сообщить коду потока, что ему пора завершаться. Одним из способов такого оповещения как раз и служит свойство Terminated и метод Terminate, которые обращаются к одному внутреннему полю.
← →
ctranik (2005-03-08 10:09) [6]
> Набережных С
Хорошо написал, если бы меня только теория интересовала , то я бы был более менне удовлетворён. НО!?
Я пишу прогу для скачивания сайтов, и загружая в этот ПОТОК компонент " MEMO" где хранится текст мною скачиного html-файла , и надо что бы в этом ПОТОКЕ прога находила html-теги типа "<a href="", а потом всё что нашла возвращала в ОСНОВНУЮ ПРОГУ
А как-ты понимаеш скачивать придется неодин фаил , то значит и ПОТОК должен работать неодин раз.
P.S. Может каким КОДОМ побалуеш,плиз
← →
Набережных С. © (2005-03-08 10:25) [7]
> ctranik (08.03.05 10:09) [6]
Нет, не побалую. Информации для решения у тебя достаточно, осталось думать и реализовывать. Это и есть работа программиста, а не халявщика. Кем ты хочешь быть - решай сам. Я вот, например, халявщиков не люблю, так уж в жизни сложилось. Ну будут конкретные вопросы - делаю вот так, но выходит не то, что хочу, а то-то и то-то - милости просим.
← →
Erik1 © (2005-03-08 10:41) [8]Кода в инете дофига, поищи в gogle TThread Delphi синхронизация потоков и пр..
← →
ctranik (2005-03-08 12:01) [9]
> Набережных С
Жаль что непобалуеш, моё решение я не ХАЛЯВЩИК.
Я нуждающийся в практическом совете.
ВОТ как выглядит моя нужда
Код Кнопки
MyThread.Editlist.Assign(memo3.Lines);//присваеваем потоку содержимое html-файла
MyThread.webpath:=WebPath;//путь до html-файла
My.resume;
Procedure Form1.Сreate
Mythread:=TmyThread.reta(true);
а вот и модуль потока
unit MyThread;
interface
uses
Classes,Podprogramy;
type
TMyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
public
Editlist:TstringList;
webpath:string;
constructor Create(CreateSuspended: Boolean);
destructor Destroy; override;
end;
implementation
uses offline;// модуль главной программы
procedure TMyThread.Execute;
label 1;
var
j,u,k,f,v,l :integer;
stroka,newstroka,url,CloseTeg,Attr:string;
begin
if editlist.Count=0 then suspended:=true;
v:=length("<a");
for j:=0 to editlist.Count-1 do begin
for u:=1 to length(EditList.Strings[j]) do begin
Attr:=""; CloseTeg:="";
//Тут ищем вхождение ТЕГА "<a" или "<A"
if (copy(EditList.Strings[j],u,v)="<a")or(copyEditList.Strings[j],u,v)="<A")then begin
stroka:=copy(EditList.Strings[j],u+v,length(EditList.Strings[j])-u);
// Тут ищем закрывающий тег
if pos("</a>",stroka)>0 then CloseTeg:="</a>"
else
if pos("</A>",stroka)>0 then CloseTeg:="</A>";
if CloseTeg<>"" then begin
l:=pos(CloseTeg,stroka);
stroka:=copy(stroka,1,l);
end;
// тут ищем аттрибут тега "HREF="" или " href=" "
if pos("href="",stroka)>0 then Attr:="href=""
else
if pos("HREF="",stroka)>0 then Attr:="HREF="" ;
if Attr<>"" then begin
f:=length(Attr);
newstroka:= copy(stroka,pos(Attr,stroka)+f,length(stroka)-(pos(Attr,stroka)+f));
if pos(""",newstroka)>0 then newstroka:=copy(newstroka,1,pos(""",newstroka)-1);
end
else newstroka:="";
// эта функция для удаление ненужных слов (обязательна)
if IgnorList(newstroka)=false then continue;
if pos("/",newstroka)=1 then newstroka:=copy(newstroka,2,length(newstroka)-pos("/",newstroka));
//тут создаётся готовый URL
url:=webpath+newstroka;
//И проверяется на наличие среди уже закаченных файлов
for k:=0 to form1.LinksList.Count-1 do begin
if url=form1.LinksList.Strings[k]then goto 1;
end;
form1.LinksList.Add(url);
end;
end;
1:end;
editlist.Clear;
end;
constructor TMyThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
Editlist := TStringList.Create;
end;
destructor TMyThread.Destroy;
begin
Editlist.Free;
inherited;
end;
end.
Хочу подчеркнуть что код метода "Execute" правильно работает,
просто он второй раз незапускается
← →
Дмитрий Мыльников (2005-03-08 12:47) [10]Объясняю популярно.
Программа, это обычно есть один главный поток. А когда ты создаёшь потомка класса TThread, то это означает, что ты собираешься в своей программе СОЗДАТЬ МНОГО ТАКИХ ПОТОКОВ. То есть, в твоём конкретном случае, как только ты что-то такое загрузил в своё Memo и нажал некю кнопку, то программа создаёт новый экземпляр твоего потока, передаёт ему копию данных, которую тот должен обработать, и в принципе может забыть о нём до тех пор, пока он не закончится. А после завершения потока он должен куда-то сложить результаты своей работы. В общем-то всё. Если у тебя одно общее хранилище данных, то тогда используй синхронизацию через CriticalSection и т.п. То есть, когда один из потоков всё внутри себя уже сделал, и собирается что-то записать в общий выходной набор данных, то он на это время приостанавливает работу всех остальных потоков, включая основную программу. Да, по этой же причине не рекомендует использовать цикл for для просмотра выходных данных, поскольку количество элементов может поменятся в то время, пока выполняется цикл, который начальное и конечое значение счётчика цикла считал в самом начале.
В приведённом тобо примере твой поток по идее не должен просто так писать куда либо, кроме внутренних переменных. А если он это делает, то нужно остальные потоки блокировать. А результаты сохранять во внутренний список, аналогичный входному. При этом он может, конечно, првоерять наличие подобных ссылок в списке формы, но не забывай, что когда ты вызываешь какую-либо процедуру из формы, то её код будет выполнятся внутри данного потока, а не в потоке главной формы. Это накладывает некоторые ограничения на подобные процедуры, в частности, они не должны использовать глобальных (по отношению к процедуре) данных, либо, опять же, нужно запускать CriticalSection, чтобы на это время заблокировать все остальные потоки.
Кстати, знаешь что мне непонятно в твоей программе? Закачка файлов дело весьма долгое, а разбор страницы достаточно быстрое (учитывая быстродействие современных компьютеров). Вот я и не могу понять, зачем создавать потоки для этой операции, а "длинне" операции потом делать снаружи.
Я бы сделал примерно так.
Вариант 1.
Потоку отдаётся в качестве входного параметра ссылка на страницу, которую собираемся закачать. Далее он эту страницу закачвает (в Memo или ещё куда, не важно), потом разбирает и формирует список ссылок, которые на странице есть. Далее, запускается критическая секция, внутри которой вызывается некая процедура, проверяющая сформированный список ссылок, одновременно заносящаяя его в общий список в главной форме, чтобы потом проверять ссылки для других потоков и иметь возможноть показать пользователю полный набор ссылок для закачки. По ходу дела эта процедура удаляет из переданного списка все ссылки, которые либо уже есть, либо не нужно закачивать. После чего управление передаётся обратно в поток, критическая секция завершается и он начинает закачивать все файлы, которые остались в списке.
Вариант 2.
Отличается тем, что страница закачивается и разбирается в основной программе, а потоку отдаются только ссылки для закачки.
Вариант 3.
Начало как в перов варианте, но после формирования списка ссылок, он возвращается в главную программу и поток на этом завершается. А для закачивания сылок создаём отдельный механизм, который запускает независимые потоки для обработки списка закачек.
Что же касается "Хочу подчеркнуть что код метода "Execute" правильно работает, просто он второй раз не запускается". То он у тебя и не будет запускаться. Для этого нужно создать ЕЩЁ ОДИН экземпляр потока. То есть, ты должен в создавать экземпляр потока не в конструкторе формы, а при нажатии кнопки. И создавать его каждый раз, когда кнопка нажимается, вот тогда у тебя и будет многопоточное приложение. А так, как это написано у тебя, толку не будет, поскольку если поток уже работает, то вызывать ему Resume смысла не имеет, а если ещё и входной набор данных изменить, не содавая CriticalSection, то результат вообще непредсказуем.
← →
Набережных С. © (2005-03-08 13:22) [11]
> ctranik (08.03.05 12:01) [9]
Судя по коду, ты даже не пытался понять написанное в [5], если вообще читал это. Ну что же, дело твое.
> Дмитрий Мыльников (08.03.05 12:47) [10]
Я бы не советовал(если бы меня кто-нибудь спросил) создавать каждый раз новый поток, если есть возможность многократно использовать уже созданный.
← →
Дмитрий Мыльников (2005-03-08 20:52) [12]>Я бы не советовал(если бы меня кто-нибудь спросил) создавать каждый раз новый поток, если есть возможность многократно использовать уже созданный.
В таком случае придётся дополнительно прдумывать и реализовывать механизм, который будет остлеживать тот факт, что какой-то из потоков уже завершил свою работу и его можно использовать повторно. А иначе мы получим вообще хрен знает что на выходе. И что делать, если поток занят? Ждать, когда он завершится?
И, опять же. либо мы пишем действительно многопотоковое приложение, либо просто заводим фоновый поток, который будет выполнять какие-то долгоиграющие операции параллельно с онвоной программой. Но на мой взгляд, это несколько разные вещи.
← →
Leonid Troyanovsky © (2005-03-08 21:11) [13]
> И, опять же. либо мы пишем действительно многопотоковое
> приложение, либо просто заводим фоновый поток, который будет
> выполнять какие-то долгоиграющие операции параллельно с
> онвоной программой. Но на мой взгляд, это несколько разные
> вещи.
Много ~ > 1.
--
Regards, LVT.
← →
Набережных С. © (2005-03-08 21:29) [14]
> Дмитрий Мыльников (08.03.05 20:52) [12]
Синхронизировать, конечно, придется, но я бы не стал говорить об этом столь трагически, дело элементарное. Да и обойдется значительно дешевле, чем создание нового потока на каждый чих.
И на выходе "хрен знает что"?, разумеется, можно получить. Но, при наличии некоторой квалификации, довольно трудно.
А впрочем, как угодно, я не настаиваю.
← →
ctranik (2005-03-09 10:21) [15]
> Набережных С.
> Дмитрий Мыльников
Ребят огромное спасибо за ваши советы , таки запистил я этот грёбанный поток. для этого в методе Execute весь свой код поместил в конструкцию
while not Terminated
...
...
end;
допёр до этого только 3-жды перечитав статью Набережного С.
(огромное спасибо).
А конструктор ПОТОКА с параметром " TRUE " так и оставил в form1.Create
А в кнопке сначала запустил этот поток и чуть ниже по коду вставил
MYThread.terminate.
И он заработал (два дня над этим грёбаным потоком долбился)
← →
ctranik (2005-03-09 10:27) [16]
> Дмитрий Мыльников
> Кстати, знаешь что мне непонятно в твоей программе? Закачка
> файлов дело весьма долгое, а разбор страницы достаточно
> быстрое (учитывая быстродействие современных компьютеров).
> Вот я и не могу понять, зачем создавать потоки для этой
> операции, а "длинне" операции потом делать снаружи.
Абсолютно согласен,сегодня-же начну реализацию этой идеи.
Хотя поначалу хотел что-бы и разбор страниц и закачка файлов всё осуществлалось с помощью потоков.
Для каждой операции свой поток.
← →
Erik1 © (2005-03-09 10:57) [17]И еще тебе тут намекли объесная теорию, что код у тебя магко говоря неправельный. А я прямо скажу в мотоже Execute у тебя глюкодром! Нельзя обращатся в нем к форме, смотри Synchronize, но учти это не лучший выход.
В Demos\Midas\Pooler несмотриш, там есть пример с критическими секциями и gogle выдает кучу програм.
← →
ctranik (2005-03-09 15:55) [18]
> Erik1 ©
Привет
Говориш
> код у тебя магко говоря неправельный. А я прямо скажу в
> мотоже Execute у тебя глюкодром! Нельзя обращатся в нем
> к форме
после некоторых поправок, благодаря господам :
Дмитрий Мыльников
Набережных С.
работае и ещё как работает, и обращение в методе Execute к различным переменным формы на Ура пролетает, и даже неспотыкается
> смотри Synchronize
а этим и вообще непользуюсь, и опять неспотыкаюсь.
А ежели всё таки неповерил могу код выслать.
и вообще всем кто пожелает.
Удачи
← →
Erik1 © (2005-03-09 17:03) [19]Удалено модератором
Примечание: Спокойнее, пожалуйста.
← →
begin...end © (2005-03-09 20:50) [20]> ctranik (09.03.05 15:55) [18]
> обращение в методе Execute к различным переменным
> формы на Ура пролетает, и даже неспотыкается
Ну что ж, Вам можно только позавидовать. Вы - везучий человек.
← →
Defunct © (2005-03-09 22:42) [21]Удалено модератором
Примечание: Цитировать тоже ни к чему.
← →
ctranik (2005-03-10 11:36) [22]
> Erik1 ©
Спасибо за теплые слова, хотя ведь написал что всё работает.
Хотя вот и > Defunct тебе тоже самое говорит.
Зря ты так, зря
← →
Набережных С. © (2005-03-10 11:45) [23]Вообще-то я не говорил, что Memo.Lines.Add потокобезопасен. Я говорил, что он не может сам по себе вызвать зависания, а это все-таки разные вещи.
← →
Ozone © (2005-03-10 11:50) [24]
> [22] ctranik (10.03.05 11:36)
Erik1 имел в виду то, что ты даже не удосужился понять и после этого проверить (после такого кол-ва теории, которую тебе написали) что есть на самом деле тот самый Synchronoze. Своей фразой он намекнул тебе на то, что если у тебя на машине (при твоих условиях работы) все "на Ура пролетает", то это не значит, что у другого пользовАтеля все будет пучком. И поверь user тебя, за твои творения, еще не так назовет...
ЗЫ: Можешь забить на мои слова, это чисто ИМХО.
← →
Defunct © (2005-03-10 19:38) [25]Набережных С. © (10.03.05 11:45) [23]
:)
Но Ваш замечательный пример с 10-ю потоками показал именно потокобезопастность memo.lines.add.
← →
Набережных С. © (2005-03-10 21:01) [26]
> Defunct © (10.03.05 19:38) [25]
Это не совсем так. Если помните, там в заголовочном вопросе ветки утверждалось, что вызов Memo.Lines.Add приводит к зависанию потока. Мой пример как раз и показывал, что именно зависания быть не может в принципе. Да это, собственно, и без примера очевидно из кода функции Add. Но из того же кода(функции Add, не примера) видно, что добавление строки производится не одним, а несколькими вызовами SendMessage, и если между этими вызовыми другой поток попытается произвести какие-то действия со строками того-же экземпляра Memo, то это может привести к потере данных. Это ситуация так-же достаточно подробно обсуждалась в той ветке, ниже моего поста с примером.
Т.е. проблема состоит в том, что одни и те же данные могут попытаться менять одновременно два разных кодовых потока.
Разумеется, можно проанализировать код этой функции и так построить алгоритм обращений, что коллизий не возникнет даже и без дополнительной синхронизации. Но я не думаю, что такой подход можно считать разумным, слишком велик риск допустить ошибку, которая проявится не сразу и в самый неподходящий момент. Да и смысла в таком подходе нет. Гораздо проще и надежней обеспечить синхронизацию доступа к Memo любым, наиболее подходящим из штатных средств.
← →
Defunct © (2005-03-10 21:25) [27]Набережных С. © (10.03.05 21:01) [26]
Согласен, в лучшем случае строка из одного потока попадет в середину строки, добавляемой другим потоком. В худшем? строка добавится не полностью? Но у него ведь (пока) всего один поток добавляет!
Однако, согласитесь, даже вероятность получения таких ошибок все равно не дает повода поступать как Erik1 в [19].
← →
Набережных С. © (2005-03-10 21:56) [28]
> Defunct © (10.03.05 21:25) [27]
Да, в [19] было, пожалуй, слишком резко. Но, знаете, я его понимаю. Ведь многие люди здесь пытаются помочь друг другу совершенно искренне. И когда встречаешься с упертостью и нежеланием прислушаться к разумным советам, когда видишь, что твои советы откротовенно игнорируют, то начинаешь чувствовать, что оказался в несколько идиотском положении. А это раздражает, и иногда сильно.
← →
Defunct © (2005-03-11 01:51) [29]> И когда встречаешься с упертостью и нежеланием прислушаться к разумным советам,
Не стоит из-за этого переживать, ведь Ваши посты читает широкая аудитория, среди которой обязательно найдутся те, кому Ваши советы пригодятся, если не в данный момент то в будущем.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2005.03.27;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.035 c