Текущий архив: 2007.03.25;
Скачать: CL | DM;
Вниз
Ничего не получается с потоками Найти похожие ветки
← →
[e]Bu$ter © (2006-06-25 22:37) [0]Возможно вопрос не совсем по теме, но подумал что раз разговор идёт об MСK, то скорее всего писать нужно сюда.
Для начала скажу, что до этого момента я ни разу с потоками не работал, поэтому, пожалуйста, сильно не ругайте если чего нибудь сильно сморожу. Общую теорию я вроде как понял, но на практике все оказалось немного сложнее чем думалось...
Посмотрел как это дело работает примере. Сложного там ничего нет (за исключением одной, непонятно зачем, используемой переменной), но там цикл, а у меня ситауция немного другая.
И так. Передо-мной стоит следующая задача:
Есть приложение, в нем есть процедура. По нажатии на одну из кнопок, нужно запускать эту процедуру.
Сама она пытается соединится с сервером, несколько раз меняет текст в строке состояния (об этом ниже), и значение одной глобальной переменной.
Мои действия:
Кидаю на форму Thread.
В onExecute пишу эту процедуру.
О обработчик нажатия кнопки вписываю вызов.... Thread.resume; Т.к. экспериментальным путем, было выяснено что Execute вызывается сразу после старта приложения (но об этом тоже ниже).
Запускаю программу, жму кнопку, всё работает. Процедура выполняется до конца.
А вот теперь грабли. Нужно чтоб когда я ещё раз нажму на эту кнопку, код метода Execute выполнился ещё раз, а этого (естественно?) не происходит.
Как я понял, код выполнился и на этом всё.
Вопрос №1: Как перезапустить поток?
Попытки вызывать Execute ни к чему хорошему не привели: второй раз процедура стартует как бы уже и не в потоке... всё подвисает.
При определённых условия (сервера не существует), процедура может повиснуть (поэтому, я её в отдельный поток решил и запихать). Нужно реализовать возможность завершать её в любое время (по желанию пользователя) той же кнопкой.
Вопрос №2: Как прибить поток?
Я пробовал Terminate, но после этого он вообще не запускается...
Ладно. Допустим всё это как-то сделать можно...
Вопрос №3: Могу ли я вызывать произвольные процедуры из программы без всяких Synchronize? Например, методы главной формы?
Проверял. Код компилируется и всё вроде как работает... но может это чем-то чревато?
Братья, наставьте меня, правды не ведающего, на путь истинный! Скажите несчастному где его лукавый попутал? Спасибо.
← →
ECM © (2006-06-26 11:47) [1]> экспериментальным путем, было выяснено что Execute вызывается
> сразу после старта приложения
Для предотвращения этого в МСК-зеркале потока есть свойство startSuspended
> код выполнился и на этом всё.
> Вопрос №1: Как перезапустить поток?
Естественно поток завершается после завершения функции Execute.
Если надо его выполнять многократно сделайте бесконечный цикл.
function TForm1.Thread1Execute(Sender: PThread): Integer;
begin
repeat
Thread1.Suspend;
... ///Тут основные действия потока
until FALSE;
end.
В этом случае для МСК надо поставить startSuspended = FALSE. Он после создания "заснет" самостоятельно.
Теперь после выполнения Resume по нажатию кнопки - поток выполнит свои действия и заснет (до следующего Resume)
> При определённых условия (сервера не существует), процедура
> может повиснуть
Ну это не есть хорошо в любом случае... Тут только Terminate, после которой поток придётся создавать заново (Тогда тут лучше без МСК). Но это уже "выкручивание рук" :)
> Вопрос №2: Как прибить поток?
Я предпочитаю делать при помощи событий - в основном потоке событие сбрасывается (Reset) перед вызовом Resume, а при необходимости прервать выполнение (тут можно прервать как одиночный цикл так и завершить поток совсем - выйти из цикла) взводится (Set). В Execute потока это событие проверяется (WaitForSingle(Multiple)Object) и при необходимости завершается (Текущая итерация или весь цикл вообще).
Вобщем главная идея в следующем - поток должен завершаться самостоятельно. Terminate - это крайняя мера.
> Вопрос №3: Могу ли я вызывать произвольные процедуры из
> программы без всяких Synchronize?
Зависит от действий выполняемых в этих процедурах.
KolBook:
Synchronize( method ) - вызывает указанный метод "синхронно", в главном потоке. Метод Synchronize следует использовать в самом выполняемом потоке для того, чтобы обеспечить более безопасное исполнение участков кода, которые желательно выполнять в контексте главного потока. Под "главным" потоком подразумевается поток команд, в котором создан оконный объект Applet (и в том же потоке должны бы создаваться и все формы, во избежание недоразумений). Данный метод обеспечивает синхронизацию путем отправки (SendMessage) сообщения главному окну (аплету), и в результате система "замораживает" поток-отправитель вплоть до возврата из переданного на выполнение в главном потоке метода. Такое замораживание не делает поток "приостановленным" (Suspended) в обычном смысле, т.к. его нельзя в этот момент "возобновить" (Resume не действует);
← →
ECM © (2006-06-26 12:47) [2]З.Ы. Еще иногда бывает удобно (для обработки потоком очереди заданий например)организовать цикл выборки сообщений в потоке. И набрасывать ему сообщения через PostThreadMessage.
Тогда кстати отпадает необходимость в Suspend-Resume и (возможно) событии
завершения потока.function TForm1.Thread1Execute(Sender: PThread): Integer;
begin
while GetMessage(Msg,0,0,0) do begin
Case Msg.message of
XXXX: begin
// ......
end;
else DispatchMessage(Msg);
end
end;
end;
Поток будет засыпать по GetMessage и просыпаться после записи события в очередь сообщений из основного потока функцией PostThreadMessage. При получении WM_QUIT цикл завершится и (соответсвенно) поток вместе с ним...
← →
[e]Buster (2006-06-26 13:04) [3]ECM, большое спасибо за развёрнутый ответ. Вопросов стало меньше, но они ещё есть.
> Ну это не есть хорошо в любом случае... Тут только Terminate,
> после которой поток придётся создавать заново (Тогда тут
> лучше без МСК). Но это уже "выкручивание рук" :)
А почему "выкручивание рук"? И намного ли это всё это сложнее без MCK?
По поводу Synchronize. Вроде как сказано, что вызвать через него можно только методы потока. Почему нельзя вызвать методы формы, скажем,
Synchronize( SetStatusText("some status text") );
Не будет работать потому что есть параметры (там с ними тоже какая-то запарка)? Или потому что это будет аналогично простому вызову SetStatusText("some status text")?
← →
[e]Buster (2006-06-26 13:21) [4]
> Поток будет засыпать по GetMessage и просыпаться после записи
> события в очередь сообщений из основного потока функцией
> PostThreadMessage. При получении WM_QUIT цикл завершится
> и (соответственно) поток вместе с ним...
Да, но если он повиснет на середине (т.е. до того, как в следующий раз начнём обрабатывать GetMessage) то просто так остановить его уже будет нельзя?
Вообще, планируется дать пользователю возможность самостоятельно задавать значение TimeOut"a по истечении которого считается что сервер не отвечает (и через таймер вызывать что нибудь, что будет прерывать поток). После этого все контролы буду разморажитьваться, чтоб пользователь снова смог на них понажимать.
Жаль что у потоков нет своего TimeOut"a, я на это очень расчитывал когда затевал это дело.
По ходу выполнения, активной будет оставаться кнопка Abort, нажатие которой в любом случае должно завершить поток (будет вызваться тоже, что и по таймеру).
И ещё. Смотрел в таскманагере пример пищалки где используется поток, так там показывает что их работает два. В моем приложении работает три потока. Откуда мог взяться третий?
← →
ECM © (2006-06-26 14:08) [5]> А почему "выкручивание рук"?
Почитайте MSDN:TerminateThread is used to cause a thread to exit. When this occurs, the target thread has no chance to execute any user-mode code and its initial stack is not deallocated. DLLs attached to the thread are not notified that the thread is terminating.
TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination.
Ну и т.д.
> намного ли это всё это сложнее без MCK?
По мне - так гораздо проще... Просто посмотрите в _1.inc формы как делает MCK - и делайте так же, но уже когда вам вздумается...:)
> По поводу Synchronize. Вроде как сказано, что вызвать через
> него можно только методы потока.
Это где такое сказано?
Можно вызывать методы. Но для Synchronize метод должен быть процедурой без параметров. А для SynchronizeEx - процедура с двумя параметрамиTThreadMethod = procedure of object;
TThreadMethodEx = procedure( Sender: PThread; Param: Pointer ) of object;
> Synchronize( SetStatusText("some status text") );
>
> Не будет работать потому что есть параметры (там с ними
> тоже какая-то запарка)?
Такую запись просто не пропустит компилятор - SetStatusText имеет параметры - а их быть не должно (см. выше). Воспользуйтесь SynchronizeEx
а строку передавайте через указатель....procedure TForm1.SetThreadStatusText(Sender: PThread; Param: Pointer);
var
S: PString;
begin
S := Param;
SetStatusText(S^);
end;
function TMainForm.Thread1Execute(Sender: PThread): Integer;
var
S: String;
begin
...
S := "1234";
Thread1.SynchronizeEx(SetThreadStatusText,@S);
...
end
← →
ECM © (2006-06-26 14:23) [6]> Да, но если он повиснет на середине (т.е. до того, как в
> следующий раз начнём обрабатывать GetMessage) то просто
> так остановить его уже будет нельзя?
Это уже ваша проблема... возможно вы что-то не так делаете, настраиваете, вызываете и т.д. Но это к работе с потоком никаким боком не относится...
> Жаль что у потоков нет своего TimeOut"a
О каком таймауте речь? Нет такого понятия у Win32 потока... Откуда оно возьмется в KOL? Если хотите - создавайте поток каждый раз когда он нужен
(без цикла в Execute) и применяйте функцию ожидания WaitForXXXX на хендле потока (Thread1.Handle) до его завершения с необходимым таймаутом. И если он (тайм-аут) сработал - убивайте поток (TerminateThread). Но это по описаным выше причинам не есть хорошо. Лучше разобраться с зависанием и попытаться предотвратить другими способами. Но это уже к потокам и KOL отношения не имеет...
> Откуда мог взяться третий?
Ну типа "на троих"... :)
Это уже вы должны определить самостоятельно... Возможно вы создали два потока.. Поставьте бряк на KOL.NewThreadXXX - пощупайте...
← →
[e]Buster (2006-06-27 12:11) [7]И так, результаты работы:
1) С зависаниями при подключении к несуществующему серверу кажется разобрался... вроде больше 3-5 сек не висит. Т.ч. тайм-аут больше не нужен - ничего наверное прибивать не придётся.
Неясно только, что получится если хост будет очень медленно отвечать на мои запросы... но это уже другая песня.
2) Понял откуда взялся третий поток: он был из одной DLL, приатаченной к основному процессу, в функции которой входит проверка правописания во всех полях ввода всех окон...
3) С однократным выполнением потока теперь тоже всё ясно. Отлично подешел этот код:
function TForm1.Thread1Execute(Sender: PThread): Integer;
begin
repeat
Thread1.Suspend;
... ///Тут основные действия потока
until FALSE;
end.
4) И с синхронизацией кажется всё в порядке!
Thread1.SynchronizeEx(SetThreadStatusText,@S); - работает как пололжено.
ECM, ну я прям не знаю чтобы я без Вас делал. ОГРОМНОЕ спасибо!
Все текущие проблемы решены и разработку, наконец, можно продолжать.
P.S. Я там скорее всего ещё буду форму настроек из DLL подгружать (в отдельном потоке :) шутка), т.ч. наверняка вернусь ;)
Страницы: 1 вся ветка
Текущий архив: 2007.03.25;
Скачать: CL | DM;
Память: 0.51 MB
Время: 0.028 c