Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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.

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

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



Страницы: 1 вся ветка

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

Наверх





Память: 0.53 MB
Время: 0.005 c
1-57813
SeF
2002-04-02 02:57
2002.04.18
Getclassname


1-57736
pusrg
2002-04-03 20:33
2002.04.18
Получение значения по указателю.


3-57615
sysoper
2002-03-28 07:10
2002.04.18
Ошибка в IB5.6 +Win2000 server+sp2


1-57781
UDS
2002-04-06 00:37
2002.04.18
Как двигать объект стрелками клавиатуры?


3-57613
vitnt2000
2002-03-19 07:31
2002.04.18
Доступ к даныым DBF (Fox)





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский