Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.04.18;
Скачать: [xml.tar.bz2];




Вниз

TThread: как вызвать его метод из основного потока и не ждать его 


lipskiy   (2002-04-03 00:35) [0]

Че-то я тормознул малость. Не могу понять, как мне из основной программы вызвать метод потока и продолжить выполнение кода основной программы, не дожидаясь завершения выполнения вызванного в потоке метода. Пока что все варианты, которыми я пробовал вызвать метод потока, приводили к тому, что основная программа ждет окончания выполнения и подвисает на это время.



Alexander Ionov   (2002-04-03 01:54) [1]

Конечно. Это основа многопоточного программирования и основная же ошибка начинающих в этой области. Если метод описан в объекте класса наследника от TThread, это не значит, что он будет вызываться в другом потоке. Для решения задачи нужно создать потомок TThread, переопределив в нем метод Execute. Потом из основного потока программы нужно создать экземпляр этого потомка TThread. При этом автоматически будет вызван метод Execute. А вот уже из Execute и следует вызывать нужный тебе метод.



lipskiy   (2002-04-03 20:32) [2]

Это все я и так знаю и это все у меня давно сделано и работает.
Поэтому и говорю, что подтормаживаю, хотя действительно, с потоком первый раз вожусь.
Есть у меня Execute и выполняется там много чего.
Но в какой-то момент мне нужно вмешаться в работу потока и заставить его выполнить некие действия.
Проще всего из основного потока выставить флажок, а в отдельном потоке этот флажок опрашивать. Но на опрос тратится лишнее время, да и интервал опроса не позволяет выполнить действие немедленно.
Нет ли иного способа?
Подчеркну - мне надо именно из основного потока вызвать метод дополнительного потока, и не ждать его выполнения.



reonid   (2002-04-03 21:24) [3]

Возможно, проблема не в том, что это метод потока,
а что он использует какие-то данные, которые также используются
в Execute потока.

Тогда тебе стоит копать в сторону критических секций, и защитить ими те данные, которые используются как в Execute, так и в методе, который ты вызываешь из главного потока.

(Лёгкость, с которой из пользовательского потока можно синхронизоваться с главным, происходит из-за того, что главный поток выполняет цикл обработки сообщений.
Сделать то же самое в обратном направлении труднее).



lipskiy   (2002-04-03 22:02) [4]

Хм, ну вот... А я то думал, что все просто.
Ну тогда воткну таймер в пользовательский поток и буду флажки опрашивать.



Fantasist   (2002-04-03 23:20) [5]

Хе. А что же тут ожтдать? Ведь если ты вызываешь метод из главного потока, этот метод будет выполнятся в главном потоке, совершенно не зависимо от того к какому классу этот метод пренадлежит. Твой второй поток как и главный имеет свой цикл, вот в нем то все и происходит. Я думаю, таймер не так здорово, лучше вначале цикла проверяй свой флажок. Типа:

prucedure MyThread.Execute()
begin
repeat
if Flag then
--делаем--
...
until Terminated;
end;



lipskiy   (2002-04-04 20:14) [6]

Проблема в том, что у меня несколько необычное использование потока. В методе Execute у меня нет цикла, как это обычно принято. В своем потоке я произвожу обновление всяких файлов через интернет, и там выполняется просто достаточно длинная и разветвленная последовательность команд. Эта последовательность выполняется один раз и поток завершается.
Ну не буду же я на каждом шагу ставить эту проверку, этак мне несколько десятков таких проверок вставить придется, если не сотен.
Да, кажется всетаки таймер...



lipskiy   (2002-04-04 20:19) [7]

Кстати вот, только что мысль пришла, нельзя ли тут как-нибудь использовать объекты-сигнализаторы?



dymka   (2002-04-04 20:20) [8]

ЭЭЭ... вопрос... если цикла нет и просто ветки там, то каким макаром там таймер???
И не случится ли такое, что таймер будет вызван в тот момент, когда ты вздумаешь менять данные потока, которые в данный момент обрабатываются??? Т.е. начнется выполнятся набор команд с одними данными, а после смены их возможно неправильное продолжение...
Я не понимаю логику вмешательства...



Fantasist   (2002-04-04 20:52) [9]

Хе! Таймер.
Чтобы использовать Timer тебе нужно окно. В твоем потоке, как я понял, окон нет, и если ты поставишь таймер в главный поток, то процедура опять же будет вызываться в главном потоке, и получишь то же самое.
Если же ты собираешься все-таки запихать таймер в свой поток(создать в нем окно), то насколько я помню, GetMessage и PeekMessage, главного потока не будут получать сообщения (WM_TIMER в твоем случае) для твоего потока, что означает, что твоя таймерская процедура не будет вызвана, если ты в своем потоке не будешь делать GetMessage. В связи с этим, я бы подумал как изменить схему работы твоего потока, и сделать в нем цикл.



lipskiy   (2002-04-05 01:33) [10]

Воткнул я таймер, все работает.
А почему, собственно оно не должно было работать?
Таймер типа TTimer создается вместе с польз. потоком и вместе с ним уничтожается, пока выполняется Execute таймер работает и проверяет флажки, если флажок изменился - обрабатывет его.
Не знаю, может чего и некорректно, но глюков нет, все выполняется как задумано во всех режимах.

По таймеру, собственно, выполняется прерывание обновления, более-то и ничего. То есть завершение потока. Но просто Terminate вызвать нельзя, так как после разрыва связи необходимо завершить корректно некоторые действия в потоке, и данные этих действий описаны локально в Execute, поэтому в OnTerminate их тоже нельзя выполнять. Нужно просто перенаправить управление внутри Execute в другую ветку, ну то есть перейти по goto на блок финализации моего обновления.
Не знаю, все ок работает.

Цикл - как его можно сделать, если нет циклических операций :)
Ну просто их нет!
К тому же сама закачка данных осуществляется сторонним компонентом TDialUp, который в свою очередь также создает себе отдельный поток.
Данные закачиваются разрозненные, разноформатные, с разных ссылок и кладутся в разные каталоги, имеют совершенно разные объемы, так что никакого цикла тут не сделаешь, как ни крути.



Fantasist   (2002-04-05 02:21) [11]

>Воткнул я таймер, все работает.
>А почему, собственно оно не должно было работать?

Интересно. А ты TTimer в каком потоке создаешь?

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

"The GetMessage function retrieves a message from the calling thread"s message queue and places it in the specified structure. This function can retrieve both messages associated with a specified window and thread messages posted via the PostThreadMessage function. The function retrieves messages that lie within a specified range of message values. GetMessage does not retrieve messages for windows that belong to other threads or applications. "

то есть по идее, главный поток не будет получать сообщение WM_TIMER так как окно таймера находиться в другом потоке. Может это надо как-то по другому понимать.

>Цикл - как его можно сделать, если нет циклических операций :)
>Ну просто их нет!

Ну я твою схему точно не знаю, ну может не совсем цикл, но что-то похожее. Типа : этап первый, второй, третий... :) Ну да ладно, это не важно.



dymka   (2002-04-05 08:43) [12]

ТАК ВОТ - как раз нужно использовать Terminate!!!!!
Функция Terminate не прекращает код потока!!!! А просто выставляет значение локальной переменной Terminated в True!
возникновение события OnTerminated не говорит о том, что поток завершится!!! Поток завершится в двух случаях -
закончится выполнение кода Execute. Второй - убъешь его извне, функцией типа KillThread...
В твоем коде после выполнения участка кода просто проверь значение переменной Terminated и если оно True, то делай свои действия для завершения работы и выходи из процедуры...



Fantasist   (2002-04-05 10:04) [13]


> ТАК ВОТ - как раз нужно использовать Terminate!!!!!
> Функция Terminate не прекращает код потока!!!! А просто
> выставляет значение локальной переменной Terminated в True!
> возникновение события OnTerminated не говорит о том, что
> поток завершится!!! Поток завершится в двух случаях -
> закончится выполнение кода Execute. Второй - убъешь его
> извне, функцией типа KillThread...
> В твоем коде после выполнения участка кода просто проверь
> значение переменной Terminated и если оно True, то делай
> свои действия для завершения работы и выходи из процедуры...


А это в какую кассу???



lipskiy   (2002-04-05 16:41) [14]

2 dymka
То есть при выставлении Terminate в true ничего не меняется вообще и Execute продолжает работать до конца?
А какая тогда разница, какой флажок выставлять и проверять - Terminate или свой собственный. Не пойму.

2 Fantasist
Таймер создаю в пользовательском потоке, а в главном мне и не надо получать от него сообщения. Обработка таймера тоже в польз. потоке. А вот флажок, объявленный в public польз. потока высталяется действительно из основного потока.
Просто получилось, что основной поток быстренько выставляет флажок и не тормозит - не ждет ничего. А таймер в польз. потоке, увидев флажок, выполняет уже внутри чего-то.



dymka   (2002-04-05 17:19) [15]

2lipskiy:

Так реализован метод класса TThread

procedure TThread.Terminate;
begin
FTerminated := True;
end;


Если уже есть флаг, то зачем использовать еще один???

2Fantastist: прочти топик еще раз сверху вниз...
хочу сказать, что можно сделать все по-другому и вроде бы на мой взгляд проще...



lipskiy   (2002-04-05 18:57) [16]

> Если уже есть флаг, то зачем использовать еще один???
Хм, тоже верно :)



Fantasist   (2002-04-05 19:10) [17]


> Таймер создаю в пользовательском потоке, а в главном мне
> и не надо получать от него сообщения. Обработка таймера
> тоже в польз. потоке. А вот флажок, объявленный в public
> польз. потока высталяется действительно из основного потока.
> Просто получилось, что основной поток быстренько выставляет
> флажок и не тормозит - не ждет ничего. А таймер в польз.
> потоке, увидев флажок, выполняет уже внутри чего-то.


Вы не о том толкуете. Как вы думаете происходит событие OnTimer? Кто-то должен вызвать WndProc твоего таймера и передпть ему сообщени WM_TIMER. После того как ты устанавливаешь таймер, винда начинает посылать сообщения WM_TIMER для окна устоновившего его. Для того чтобы получить сообщение из очереди, нужно вызвать GetMessage(PeekMessage)... Ну в общем в двух словах не скажешь, почитайте SDK. Исходники VCL посмотрите. Ну не важно.

2dimka:
Честно говоря не ожидал, что это для кого-то внове. А что же по вашему происходило когда вы делаете:
while not Terminated do
begin ... end;
Это вроде очевидно. Да и в хелпе написанно всего два предложение по методу Terminate и сказанно там именно это.
Но тем не менее как это относиться к:
"что можно сделать все по-другому и вроде бы на мой взгляд проще..."
не улавливаю. Я вообще не знаю, что там за код, который обходиться без цикла, и тем не менее чего-то там долго делает, и как он прерывается по таймеру. Чего там можно сделать проще и по другому, когда вообще не понятно что делать? Или вы что, предлагаете в качестве флага проверять Terminate? Это конечно прикольно, но чем это отличается от проверки любого другово флага?



lipskiy   (2002-04-05 19:30) [18]

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

Что там у меня делается? Да, грубо говоря, выполняется независимая программа автоматического обновления данных через инет по заданным правилам. Что-то вроде какого-нибудь Регета, которому задали список ссылок и он их качает, только при этом он качает не только файлы, но еще и списки других ссылок, открывает их и закачивает уже файлы по ссылкам из списка.
Какой тут цикл и зачем он вообще обязательно нужен?
Это просто ветвящаяся процедура длительного выполнения с анализирующей логикой if then и т.п.



Fantasist   (2002-04-05 21:05) [19]


> Основной то поток не обязан только в цикле работать


Основной поток всегда в цикле работает. Не обязан он, конечно, но все GUI программы именно так и делают - этот цикл принимает сообщения от виндос пока не встрениться WM_QUIT после чего происходит тот самый пресловутый Terminate, то есть выходит из цикла.


> Это просто ветвящаяся процедура длительного выполнения с
> анализирующей логикой if then и т.п.


Ну это я примерно понял, но! Почему она долго выполняется? Если какая-то процедура долго выполняется, то в контексте данного потока ничего другого вызванно быть не может, пока она не закончиться. Может там порождается еще один поток, который сам долго крутиться. Ну в общем, не хочу судить о том, чего не видел.

> Какой тут цикл и зачем он вообще обязательно нужен?

Да нет не обязательно. Но как я понял у тебя уже все работает, то че мы тут тогда базарим? ;)



lipskiy   (2002-04-05 21:56) [20]

Да базарим то для общего развития :)
Если у меня работает, то это еще не значит, что это правильно, и что при других условиях (на другой машине, на другой винде) все будет также хорошо работать.

Долго выполняется сама закачка.
Да, там порождается свой поток, это верно.
Я использую компонент закачки файлов по HTTP - DialUp, и он качает в своем потоке. Прервать этот поток я могу корректно - для этого у компонента закачки есть метод HangUp. Но этот поток создается многократно и многократно уничтожается - на закачку каждого файла компонент закачки создает новый поток. И в промежутках между закачками файлов я выполняю некоторую логику, а также могу, например, проверить наличие связи с интернетом - не пропала ли и т.п. Так вот я хочу, чтобы эти промежуточные действия тоже не подтормаживали основной поток, поэтому и создаю свой поток - некий посредник между основным потоком и потоком закачки в компоненте DialUp.

И теперь - из основного потока я желаю прервать обновление - я должен послать команду своему потоку, а он свою очередь посылает команду потоку закачки - все прерывается и завершается. Процесс прерывания коннекта занимает неск. секунд и я не желаю в основном потоке ждать этого. Поэтому и завел речь о таймерах и прочей бодяге.

В общем, можно уже и не отвечать.
Но если есть какие светлые мысли - мне все равно будет интересно :)
В любом случае, всем спасибо!




Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.04.18;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.8 MB
Время: 0.032 c
1-57665           VID                   2002-04-03 23:02  2002.04.18  
Чтение значения переменной, созданной другой программой


3-57636           koks                  2002-03-26 13:45  2002.04.18  
ADO -> Access 2000 ???? HEEEEEEEEELP !


1-57749           vadim2                2002-04-05 11:24  2002.04.18  
а как узнать что drag&drop успешно выполнилось? например:


1-57785           Sergey_R              2002-04-05 18:36  2002.04.18  
Нетрадиционный вопрос!


1-57804           ZPS                   2002-04-06 00:38  2002.04.18  
PopupMenu1 - поменять цвет ?