Опять потоки

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Опять потоки

Сообщение ronin » 12.03.2010 17:02:24

Извините меня, я возможно уже достал всех, но с потоками всё таки работаю первый раз, так что сильно не ругать :)

Вопрос в следующем, использую один единственный поток (не основной) для загрузки файлов с сети с использованием Indy. Программа циклом проходит по строкам таблицы с ссылками и в потоке скачивает файлы из интернета. Запуск и остановку потока по нажатию кнопок я отработал, а вот корректную остановку потока по закрытию программы в случае закачки файла так и не могу сделать.

Для записи в файл использую TFileStream, соответственно выполняется процедура HTTP.Get(link,file), где переменная file как раз и является файловым потоком. Основная задача обеспечить докачку файлов с сервера, с этим я тоже справился с помощью указания Range компонента idHTTP. Проблема именно в остановке закачки при закрытии формы.

Остановка закачки, то есть выполнения потока происходит либо при окончании скачивания файла (либо диапазона, т.е докачки), т.е. выполнения процедуры Get, либо после установки булевой переменной в false, которая проверяется на событии OnWork компонента idHTTP. На закрытие приложения я добавил установку булевой переменной в false, как при остановке закачки, соответственно должна происходить отработка потока и уничтожение, а соответственно и всех его объектов (тот самы файловый поток), но этого не происходит, точнее поток не успевает отработать, соответсвенно не происходит освобождения файлового потока, и после закрытия программы файл, в который происходила закачка имеет размер файла источника, а не закачанной части. Т.е. получается не происходит уничтожения переменной file.

Так же есть косячок, который я до сих пор не могу понять. При создании потока устанавливаю FreeOnTerminate=true, соответственно поток должен при отработке процедуры Execute уничтожаться автоматически. После остановки закачки по нажатию кнопки в приложении, закачка останавливается, переменная file уничтожается (в конструкции finally в потоке), и файл приёмник имеет корректный размер, который мы успели закачать. Но при попытке проверки существования потока if Assigned(GetThread) программа показывает что потока существует, т.е. он не уничтожился, как и ожидалось.

У меня два вопроса:

1) как корректно убить поток, что бы произошло уничтожение фалового потока и сохранение именно закачанного объёма информации в файл? пока что у меня это не получается
2) почему при остановке потока не происходит его уничтожение? или я не правильно проверяю?
ronin
постоялец
 
Сообщения: 174
Зарегистрирован: 27.01.2010 00:14:46

Re: Опять потоки

Сообщение hinst » 12.03.2010 17:48:13

1. Делаешь Terminate, Terminated становится true, ждёшь, пока твой поток, сообразив, что он должен уничтожится, завершит работу WaitFor'ом
2. уничтожение происходит, просто указатель на него не становится nil
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Опять потоки

Сообщение ronin » 13.03.2010 11:04:20

уничтожение происходит, просто указатель на него не становится nil


понятно

Делаешь Terminate, Terminated становится true, ждёшь, пока твой поток, сообразив, что он должен уничтожится, завершит работу WaitFor'ом


при таком раскладе при выполнении GetThread.WaitFor происходит зависание программы, приходится её убивать вручную, да и какая разница между Terminate и моей переменной, ведь Terminate всего лишь переводит terminated в true, соответственно пока поток не отработает, остановки не будет, я могу конечно добавить проверку if not Terminated, но какая разница между if Downloading=true (та самая переменная)

пытался применить WaitForThreadTerminate, тоже зависает... в общем любая попытка обратиться к потоку при закрытии приложения, как например GetThread.Free, или FreeAndNil(GetThread) вызывает зависание программы, хотя поток точно живой и работает в этот момент

Добавлено спустя 35 минут 20 секунд:
погуглил тут пару дней, такой вопрос раз десять встречал, в результате только споры и никаких конкретных решений, перепробовал уже разные варианты, ничего не получается

По логике как:

Создаём поток, ставим FreeOnTerminate=true, соответственно при отработке потока произойдёт его уничожение, пишем процедуру Execute, циклов там пока нет, просто выполнение функции Get. Закачку производим в файловый поток, по окончании закачки в конструкции finally в процедуре Execute потока уничтожаем объект файлового потока и idHTTP, предварительно сделав дисконнект. Закачка файла может быть либо выполнена полностью, либо остановлена после присваивания переменной Downloading значения true, которая проверяется в функции OnWork объекта idHTTP при закачивании очередной порции файла.

В итоге есть две кнопки, одна создаёт поток и запускает его, вторая присваивает переменной Downloading значение false, и соответственно происходит завершение работы потока, всё работает корректно, закачка, докачка, запуск, останов.

При закрытии формы проверяем переменную Downloading, если значение true, то нужно остановить закачку и завершить работу программы. Но в этом случае поток не успевает отработать процедуру Execute до конца, хотя я присваиваю Downloading=false, соответственно не происходит дисконнекта и уничтожения объектов потока и файл получатель имеет размер файла источника, и при следующем запуске загрузки мы получаем ошибку, потому что файл не был докачан до конца, но размер равен источнику.

Задача: корректно завершить работу потока, уничтожить все его объекты и отпустить файловый поток.

Надеюсь я всё правильно понимаю?
ronin
постоялец
 
Сообщения: 174
Зарегистрирован: 27.01.2010 00:14:46

Re: Опять потоки

Сообщение Sergei I. Gorelkin » 13.03.2010 12:25:31

1) Прерывание потока с помощью переменной (Terminated, Downloading, неважно) хорошо работает только тогда, когда поток фактически занимается только вычислениями и проверяет эту переменную часто. Если же поток ждет какого-то события, установка любых переменных ему совершенно по барабану, нужно "будить". Иначе да, при вызове WaitFor будет висеть. Эта часть напрочь отсутствует в функциях TThread, ее нужно реализовывать самостоятельно.
2) Забудь про FreeOnTerminate=true как страшный сон. Это вообще ничего не дает, кроме геморроя с оставшимися невалидными указателями.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Опять потоки

Сообщение ronin » 13.03.2010 14:28:38

Если же поток ждет какого-то события, установка любых переменных ему совершенно по барабану


в потоке идёт закачка файла объектом idHTTP порциями, соответственно при скачивании каждой порции идёт проверка переменной Downloading, проблема в том что при закрытии приложения эта проверка скорее всего не успевает пройти и процедура Execute не отрабатывает до конца, задача и состоит в том чтобы дождаться окончания работы потока, т.е. докачки закачиваемой на данный момент порции данных и уничтожения объектов потока

а вот как это сделать не могу придумать, пытался даже вручную уничтожить эти объекты, при корректной отработке данной процедуры файл приемник на винчестере должен соответствовать размеру закачанной информации на данный момент, а не полному размеру файла источника, но не срабатывает, почему то поток не отпускает файл

Добавлено спустя 2 минуты 45 секунд:
кроме геморроя с оставшимися невалидными указателями


в конструкции finally в процедуре выполнения потока выполняю FreeAndNil для всех созданных в нём объектов, так что тут думаю проблем быть не должно
ronin
постоялец
 
Сообщения: 174
Зарегистрирован: 27.01.2010 00:14:46

Re: Опять потоки

Сообщение Sergei I. Gorelkin » 13.03.2010 15:22:30

ronin писал(а):в потоке идёт закачка файла объектом idHTTP порциями, соответственно при скачивании каждой порции идёт проверка переменной Downloading, проблема в том что при закрытии приложения эта проверка скорее всего не успевает пройти и процедура Execute не отрабатывает до конца, задача и состоит в том чтобы дождаться окончания работы потока, т.е. докачки закачиваемой на данный момент порции данных и уничтожения объектов потока

Я не понимаю, как оно может не отработать до конца, если не используются вызовы типа windows.TerminateThread/KillThread. Устанавливаешь downloading:=false, вызываешь WaitFor, программа будет ждать, пока поток не доработает до конца.

ronin писал(а):в конструкции finally в процедуре выполнения потока выполняю FreeAndNil для всех созданных в нём объектов, так что тут думаю проблем быть не должно

Проблема с указателем на сам объект потока. Ты не можешь из основного потока вызвать WaitFor рабочего потока, если он к тому моменту уже самоуничтожился.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Опять потоки

Сообщение hinst » 13.03.2010 19:08:51

я когда делал чего-то с потоками, делал эту хрень в такой последовательности:
1. th.Terminate
2. th.WaitFor
3. FreeAndNil(th)
и вроде-бы нормально было, без FreeOnTerminate
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Опять потоки

Сообщение ronin » 14.03.2010 09:30:21

я когда делал чего-то с потоками, делал эту хрень в такой последовательности:
1. th.Terminate
2. th.WaitFor
3. FreeAndNil(th)
и вроде-бы нормально было, без FreeOnTerminate


спасибо за подсказку, это действительно правильный вариант, именно то что нужно

а проблема оказалась в том что в потоке я использую синхронизацию (Synchronize) с основным потоком при обращении к компонентам главной формы, и при вызове метода WaitFor происходит зависание программы, потому что при использовании синхронизации дочерний поток ждёт главный поток для обращения к VCL, а главный поток в свою очередь ждёт дочерний, здесь и был тупик

теперь буду думать как выполнить синхронизацию потоков
ronin
постоялец
 
Сообщения: 174
Зарегистрирован: 27.01.2010 00:14:46

Re: Опять потоки

Сообщение ronin » 15.03.2010 09:57:55

Итак, проблему я выявил, а вот как решить опять не знаю. Пробовал использовать критические секции, например:

Код: Выделить всё
Section.Enter;

работа потока...

Section.Leave;


не помогает, всё равно не происходит корректного завершения потока

так же пробовал создавать event, чё то не фурычит, так до конца и не понял как прибить поток

У меня вопрос: есть у кого опыт синхронизации основного и дочернего потоков, т.е. ожидание завершения работы дочернего потока?

P.S. вырубив всю синхронизацию в дочернем потоке метод WaitFor отрабатывает, но ведь как без Synchronize то работать? разве можно?
ronin
постоялец
 
Сообщения: 174
Зарегистрирован: 27.01.2010 00:14:46

Re: Опять потоки

Сообщение Sergei I. Gorelkin » 15.03.2010 10:44:11

А зачем из потока звать synchronize, если он видит, что установлен флаг завершения?
Завершайся, гуйню потом из основного потока обновишь, если нужно.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Опять потоки

Сообщение ronin » 15.03.2010 10:53:54

А зачем из потока звать synchronize, если он видит, что установлен флаг завершения?


Synchronize использую для отображения прогресса закачки (обращение к VCL), и вывода сообщений пользователю об этапах выполнения загрузки - коннект/загрузка/окончание/ошибки...

можно обойтись без synchronize? в принципе у меня один поток закачки

P.S. во всех источниках что прочитал пугали о синхронизации, о возможности появления ошибок при одновременном обращении к VCL
ronin
постоялец
 
Сообщения: 174
Зарегистрирован: 27.01.2010 00:14:46

Re: Опять потоки

Сообщение Sergei I. Gorelkin » 15.03.2010 11:06:03

Я говорю не об отказе от synchronize вообще (это отдельный вопрос), а об отказе от них в тот момент, когда рабочий поток должен завершиться, а основной ждет его на WaitFor.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Опять потоки

Сообщение ronin » 15.03.2010 11:21:13

а как это сделать? как заставить отказаться от выполнения метода Synchronize в момент закрытия приложения? вопрос наверное глупый, но всё же
ronin
постоялец
 
Сообщения: 174
Зарегистрирован: 27.01.2010 00:14:46

Re: Опять потоки

Сообщение Sergei I. Gorelkin » 15.03.2010 11:56:46

Ну хотя бы так.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Опять потоки

Сообщение ronin » 15.03.2010 12:29:21

???

а вообще, какие то костыли получаются, или я не прав?
ronin
постоялец
 
Сообщения: 174
Зарегистрирован: 27.01.2010 00:14:46

След.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 39

Рейтинг@Mail.ru