![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Работаем с Thread-ми , очень нужно [D6, WinXP]
ctranik © (2005-03-07 14:58:00)Всем привет
Требуется передавать данные во время работы ПОТОКА.
Предположим, что превоначально в этот ПОТОК загрузили текст
из компонента " Memo1 " (обычный текст).
Теперь содержимое компонента " Memo1 " изменилось(а поток-то работает!!! ).
Как новые данные загрузить в работающий ПОТОК ?
Defunct (2005-03-11 01:51:00) [1]> И когда встречаешься с упертостью и нежеланием прислушаться к разумным советам,
Не стоит из-за этого переживать, ведь Ваши посты читает широкая аудитория, среди которой обязательно найдутся те, кому Ваши советы пригодятся, если не в данный момент то в будущем.
Набережных С. (2005-03-10 21:56:00) [2]
> Defunct © (10.03.05 21:25) [27]
Да, в [19] было, пожалуй, слишком резко. Но, знаете, я его понимаю. Ведь многие люди здесь пытаются помочь друг другу совершенно искренне. И когда встречаешься с упертостью и нежеланием прислушаться к разумным советам, когда видишь, что твои советы откротовенно игнорируют, то начинаешь чувствовать, что оказался в несколько идиотском положении. А это раздражает, и иногда сильно.
Defunct (2005-03-10 21:25:00) [3]Набережных С. © (10.03.05 21:01) [26]
Согласен, в лучшем случае строка из одного потока попадет в середину строки, добавляемой другим потоком. В худшем? строка добавится не полностью? Но у него ведь (пока) всего один поток добавляет!
Однако, согласитесь, даже вероятность получения таких ошибок все равно не дает повода поступать как Erik1 в [19].
Набережных С. (2005-03-10 21:01:00) [4]
> Defunct © (10.03.05 19:38) [25]
Это не совсем так. Если помните, там в заголовочном вопросе ветки утверждалось, что вызов Memo.Lines.Add приводит к зависанию потока. Мой пример как раз и показывал, что именно зависаниябыть не может в принципе. Да это, собственно, и без примера очевидно из кода функции Add. Но из того же кода(функции Add, не примера) видно, что добавление строки производится не одним, а несколькими вызовами SendMessage, и если между этими вызовыми другой поток попытается произвести какие-то действия со строками того-же экземпляра Memo, то это может привести к потере данных. Это ситуация так-же достаточно подробно обсуждалась в той ветке, ниже моего поста с примером.
Т.е. проблема состоит в том, что одни и те же данные могут попытаться менять одновременнодва разных кодовых потока.
Разумеется, можно проанализировать код этой функции и так построить алгоритм обращений, что коллизий не возникнет даже и без дополнительной синхронизации. Но я не думаю, что такой подход можно считать разумным, слишком велик риск допустить ошибку, которая проявится не сразу и в самый неподходящий момент. Да и смысла в таком подходе нет. Гораздо проще и надежней обеспечить синхронизацию доступа к Memo любым, наиболее подходящим из штатных средств.
Defunct (2005-03-10 19:38:00) [5]Набережных С. © (10.03.05 11:45) [23]
:)
Но Ваш замечательный пример с 10-ю потоками показал именно потокобезопастность memo.lines.add.
Ozone (2005-03-10 11:50:00) [6]
> [22] ctranik (10.03.05 11:36)
Erik1 имел в виду то, что ты даже не удосужился понять и после этого проверить (после такого кол-ва теории, которую тебе написали) что есть на самом деле тот самый Synchronoze. Своей фразой он намекнул тебе на то, что если у тебя на машине (при твоих условиях работы) все "на Ура пролетает", то это не значит, что у другого пользовАтеля все будет пучком. И поверь user тебя, за твои творения, еще не так назовет...
ЗЫ: Можешь забить на мои слова, это чисто ИМХО.
Набережных С. (2005-03-10 11:45:00) [7]Вообще-то я не говорил, что Memo.Lines.Add потокобезопасен. Я говорил, что он не может сам по себе вызвать зависания, а это все-таки разные вещи.
ctranik (2005-03-10 11:36:00) [8]
> Erik1 ©
Спасибо за теплые слова, хотя ведь написал что всё работает.
Хотя вот и > Defunctтебе тоже самое говорит.
Зря ты так, зря
Defunct (2005-03-09 22:42:00) [9]Удалено модератором
Примечание: Цитировать тоже ни к чему.
begin...end (2005-03-09 20:50:00) [10]> ctranik (09.03.05 15:55) [18]
> обращение в методе Execute к различным переменным
> формы на Ура пролетает, и даже неспотыкается
Ну что ж, Вам можно только позавидовать. Вы - везучий человек.
Erik1 (2005-03-09 17:03:00) [11]Удалено модератором
Примечание: Спокойнее, пожалуйста.
ctranik (2005-03-09 15:55:00) [12]
> Erik1 ©
Привет
Говориш
> код у тебя магко говоря неправельный. А я прямо скажу в
> мотоже Execute у тебя глюкодром! Нельзя обращатся в нем
> к форме
после некоторых поправок, благодаря господам :
Дмитрий Мыльников
Набережных С.
работае и ещё как работает, и обращение в методе Execute к различным переменным формы на Ура пролетает, и даже неспотыкается
> смотри Synchronize
а этим и вообще непользуюсь, и опять неспотыкаюсь.
А ежели всё таки неповерил могу код выслать.
и вообще всем кто пожелает.
Удачи
Erik1 (2005-03-09 10:57:00) [13]И еще тебе тут намекли объесная теорию, что код у тебя магко говоря неправельный. А я прямо скажу в мотоже Execute у тебя глюкодром! Нельзя обращатся в нем к форме, смотри Synchronize, но учти это не лучший выход.
В Demos\Midas\Pooler несмотриш, там есть пример с критическими секциями и gogle выдает кучу програм.
ctranik (2005-03-09 10:27:00) [14]
> Дмитрий Мыльников
> Кстати, знаешь что мне непонятно в твоей программе? Закачка
> файлов дело весьма долгое, а разбор страницы достаточно
> быстрое (учитывая быстродействие современных компьютеров).
> Вот я и не могу понять, зачем создавать потоки для этой
> операции, а "длинне" операции потом делать снаружи.
Абсолютно согласен,сегодня-же начну реализацию этой идеи.
Хотя поначалу хотел что-бы и разбор страниц и закачка файлов всё осуществлалось с помощью потоков.
Для каждой операции свой поток.
ctranik (2005-03-09 10:21:00) [15]
> Набережных С.
> Дмитрий Мыльников
Ребят огромное спасибо за ваши советы , таки запистил я этот грёбанный поток. для этого в методе Execute весь свой код поместил в конструкцию
while not Terminated
...
...
end;
допёр до этого только 3-жды перечитав статью Набережного С.
(огромное спасибо).
А конструктор ПОТОКА с параметром " TRUE " так и оставил в form1.Create
А в кнопке сначала запустил этот поток и чуть ниже по коду вставил
MYThread.terminate.
И он заработал (два дня над этим грёбаным потоком долбился)
Набережных С. (2005-03-08 21:29:00) [16]
> Дмитрий Мыльников (08.03.05 20:52) [12]
Синхронизировать, конечно, придется, но я бы не стал говорить об этом столь трагически, дело элементарное. Да и обойдется значительно дешевле, чем создание нового потока на каждый чих.
И на выходе "хрен знает что"?, разумеется, можно получить. Но, при наличии некоторой квалификации, довольно трудно.
А впрочем, как угодно, я не настаиваю.
Leonid Troyanovsky (2005-03-08 21:11:00) [17]
> И, опять же. либо мы пишем действительно многопотоковое
> приложение, либо просто заводим фоновый поток, который будет
> выполнять какие-то долгоиграющие операции параллельно с
> онвоной программой. Но на мой взгляд, это несколько разные
> вещи.
Много ~ > 1.
--
Regards, LVT.
Дмитрий Мыльников (2005-03-08 20:52:00) [18]>Я бы не советовал(если бы меня кто-нибудь спросил) создавать каждый раз новый поток, если есть возможность многократно использовать уже созданный.
В таком случае придётся дополнительно прдумывать и реализовывать механизм, который будет остлеживать тот факт, что какой-то из потоков уже завершил свою работу и его можно использовать повторно. А иначе мы получим вообще хрен знает что на выходе. И что делать, если поток занят? Ждать, когда он завершится?
И, опять же. либо мы пишем действительно многопотоковое приложение, либо просто заводим фоновый поток, который будет выполнять какие-то долгоиграющие операции параллельно с онвоной программой. Но на мой взгляд, это несколько разные вещи.
Набережных С. (2005-03-08 13:22:00) [19]
> ctranik (08.03.05 12:01) [9]
Судя по коду, ты даже не пытался понять написанное в [5], если вообще читал это. Ну что же, дело твое.
> Дмитрий Мыльников (08.03.05 12:47) [10]
Я бы не советовал(если бы меня кто-нибудь спросил) создавать каждый раз новый поток, если есть возможность многократно использовать уже созданный.
Дмитрий Мыльников (2005-03-08 12:47:00) [20]Объясняю популярно.
Программа, это обычно есть один главный поток. А когда ты создаёшь потомка класса TThread, то это означает, что ты собираешься в своей программе СОЗДАТЬ МНОГО ТАКИХ ПОТОКОВ. То есть, в твоём конкретном случае, как только ты что-то такое загрузил в своё Memo и нажал некю кнопку, то программа создаёт новый экземпляр твоего потока, передаёт ему копию данных, которую тот должен обработать, и в принципе может забыть о нём до тех пор, пока он не закончится. А после завершения потока он должен куда-то сложить результаты своей работы. В общем-то всё. Если у тебя одно общее хранилище данных, то тогда используй синхронизацию через CriticalSection и т.п. То есть, когда один из потоков всё внутри себя уже сделал, и собирается что-то записать в общий выходной набор данных, то он на это время приостанавливает работу всех остальных потоков, включая основную программу. Да, по этой же причине не рекомендует использовать цикл for для просмотра выходных данных, поскольку количество элементов может поменятся в то время, пока выполняется цикл, который начальное и конечое значение счётчика цикла считал в самом начале.
В приведённом тобо примере твой поток по идее не должен просто так писать куда либо, кроме внутренних переменных. А если он это делает, то нужно остальные потоки блокировать. А результаты сохранять во внутренний список, аналогичный входному. При этом он может, конечно, првоерять наличие подобных ссылок в списке формы, но не забывай, что когда ты вызываешь какую-либо процедуру из формы, то её код будет выполнятся внутри данного потока, а не в потоке главной формы. Это накладывает некоторые ограничения на подобные процедуры, в частности, они не должны использовать глобальных (по отношению к процедуре) данных, либо, опять же, нужно запускать CriticalSection, чтобы на это время заблокировать все остальные потоки.
Кстати, знаешь что мне непонятно в твоей программе? Закачка файлов дело весьма долгое, а разбор страницы достаточно быстрое (учитывая быстродействие современных компьютеров). Вот я и не могу понять, зачем создавать потоки для этой операции, а "длинне" операции потом делать снаружи.
Я бы сделал примерно так.
Вариант 1.
Потоку отдаётся в качестве входного параметра ссылка на страницу, которую собираемся закачать. Далее он эту страницу закачвает (в Memo или ещё куда, не важно), потом разбирает и формирует список ссылок, которые на странице есть. Далее, запускается критическая секция, внутри которой вызывается некая процедура, проверяющая сформированный список ссылок, одновременно заносящаяя его в общий список в главной форме, чтобы потом проверять ссылки для других потоков и иметь возможноть показать пользователю полный набор ссылок для закачки. По ходу дела эта процедура удаляет из переданного списка все ссылки, которые либо уже есть, либо не нужно закачивать. После чего управление передаётся обратно в поток, критическая секция завершается и он начинает закачивать все файлы, которые остались в списке.
Вариант 2.
Отличается тем, что страница закачивается и разбирается в основной программе, а потоку отдаются только ссылки для закачки.
Вариант 3.
Начало как в перов варианте, но после формирования списка ссылок, он возвращается в главную программу и поток на этом завершается. А для закачивания сылок создаём отдельный механизм, который запускает независимые потоки для обработки списка закачек.
Что же касается "Хочу подчеркнуть что код метода "Execute" правильно работает, просто он второй раз не запускается". То он у тебя и не будет запускаться. Для этого нужно создать ЕЩЁ ОДИН экземпляр потока. То есть, ты должен в создавать экземпляр потока не в конструкторе формы, а при нажатии кнопки. И создавать его каждый раз, когда кнопка нажимается, вот тогда у тебя и будет многопоточное приложение. А так, как это написано у тебя, толку не будет, поскольку если поток уже работает, то вызывать ему Resume смысла не имеет, а если ещё и входной набор данных изменить, не содавая CriticalSection, то результат вообще непредсказуем.