Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2006.11.05;
Скачать: [xml.tar.bz2];

Вниз

Ожидание потока   Найти похожие ветки 

 
Destroyer ©   (2006-09-23 13:43) [0]

Программа запускает поток на выполнение действий. Запускать поток надо много раз на выполнение разных действий. Проблема в том, что надо дождаться выполнения предыдущего действия в потоке (чтобы поток уничтожился), а затем опять его запускать. Иначе запуск потока для нескольких задач одновременно выдает ошибку (естесственно).
Делаю так: в основной подпрограмме перед вызовами потока поставил процедуру ожидания освобождения потока. В потоке, при его завершении, выставляется флаг: ThreadBusy := false. Но это не помогает.
procedure WaitForFreeThread;
begin
while ThreadBusy do
begin
  sleep(100);
end;
end;
...
TimerThread.Create("параметры");
// здесь вставил процедуру ожидания
WaitForFreeThread;
TimerThread.Create("параметры");
// здесь вставил процедуру ожидания
WaitForFreeThread;
TimerThread.Create("параметры");
...
Проблема в том, что программа зацикливается в WaitForFreeThread.
Как правильно дождаться завершения потока?


 
Virgo_Style ©   (2006-09-23 13:47) [1]

TThread.OnTerminate ?


 
Virgo_Style ©   (2006-09-23 13:48) [2]

А подход интересный, Архангельский кусает локти)))


 
Palladin ©   (2006-09-23 13:51) [3]

а на кой ляд тут нужны потоки?


 
Virgo_Style ©   (2006-09-23 13:54) [4]

Palladin ©   (23.09.06 13:51) [3]
а на кой ляд тут нужны потоки?


Для размораживания интерфейса?
Если судить по тому, что написано черными буковками, а не синими %-)


 
Destroyer ©   (2006-09-23 14:07) [5]

Без потока не обойтись. Да для того, чтобы интерфейс был активен, чтобы в любой момент пользователь мог нажать кнопку остановки.
Вопрос остается в силе: есть последовательность вызовов одного и тогоже потока. Как после предыдущего вызова дождаться окончания потока, чтобы продолжить перейти на следующий вызов?


 
Destroyer ©   (2006-09-23 14:11) [6]

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


 
y-soft ©   (2006-09-23 14:41) [7]

>Destroyer ©   (23.09.06 14:07) [5]

Вот это - извращение:

procedure WaitForFreeThread;
begin
while ThreadBusy do
begin
 sleep(100);
end;
end;


Лучше уж тогда WaitForSingleObject(TimerThread.Handle, INFINITE), а если нужна еще и возможность завершить управляющий поток не дожидаясь окончания инструментального, то WaitForMultipleObjects или MsgWaitForMultipleObjects...

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

Иначе запуск потока для нескольких задач одновременно выдает ошибку (естесственно).

И совсем не естественно - не зацикливайтесь на единственном потоке и правильно применяйте синхронизацию, и не будет никаких ошибок


 
y-soft ©   (2006-09-23 15:11) [8]

>Destroyer ©   (23.09.06 14:11) [6]

Но просто есть много процедур, которые могут выполняться отдельно (в них этот поток запускается с разными параметрами - разные кнопки на форме). Но есть нужда в том, чтобы все эти процедуры запустить последовательно, одним нажатием кнопки на форме

Не зацикливайтесь на единственном инструментальном потоке!

Сделайте примерно так:

1. Определите глобальную переменные

var
 RunningTaskCount: integer = 0;
 CompleteEvent: THandle = 0;


2. При запуске приложения создайте событие с ручным сбросом:

 CompleteEvent := Create(Event, True, True, nil);

3. Определите также пару глобальных процедур:


procedure IncCounter;
begin
 if InterlockedExchangeAdd(@RunningTaskCount, 1) = 0 then
   ResetEvent(CompleteEvent);
end;

procedure DecCounter;
begin
  if InterlockedExchangeAdd(@RunningTaskCount, -1) = 1 then
    SetEvent(CompleteEvent);
end;


4. Теперь запускайте сколько вам надо независимых инструментальных потоков, только установите для них FreeOnTerminate := True - хоть сразу, хоть по одиночке в любом порядке;

только в начале метода Execute вызывайте IncCounter, а в конце - DecCounter,
и ни о чем не беспокойтесь :)

5. При завершении приложения выполните следующий код:

 WaitForSingleObject(CompleteEvent, INFINITE);
 CloseHandle(ComleteEvent);


Успехов!


 
y-soft ©   (2006-09-23 15:13) [9]

CompleteEvent := Create(Event, True, True, nil);

Сорри за опечатку, следует читать:

CompleteEvent := CreateEvent(nil, True, True, nil);


 
Destroyer ©   (2006-09-23 18:51) [10]

Спасибо за разъяснение. Но остались проблемы. Следовал инструкциям: в основной программе сделал глобальные переменные
RunningTaskCount: integer = 0;
CompleteEvent: THandle = 0;

> 2. При запуске приложения создайте событие с ручным сбросом:
>
>
>  CompleteEvent := Create(Event, True, True, nil);


Это обязательно при запуске основной программы? Или можно позже? Вроде нет разницы.

Добавил
CompleteEvent := Create(Event, True, True, nil);
в обработчик нажатия кнопки, которая запускает последовательные вызовы потока.

Добавил в основную программу 2 процедуры
procedure IncCounter;
begin
if InterlockedExchangeAdd(@RunningTaskCount, 1) = 0 then
  ResetEvent(CompleteEvent);
end;

procedure DecCounter;
begin
 if InterlockedExchangeAdd(@RunningTaskCount, -1) = 1 then
   SetEvent(CompleteEvent);
end;

Сделал их глобальными

FreeOnTerminate := True  поставил в методе Create потока.
В начале метода Execute вызываю IncCounter, в конце - DecCounter.


> 5. При завершении приложения выполните следующий код:
>
>  WaitForSingleObject(CompleteEvent, INFINITE);
>  CloseHandle(ComleteEvent);


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


 
y-soft ©   (2006-09-23 19:29) [11]

Это обязательно при запуске основной программы? Или можно позже? Вроде нет разницы.


Это обязательно сделать до запуска потоков, поэтому и советую сделать в начале кода программы. Удобное место, например, событие OnCreate главной формы, но никак не обработчики кнопок, запускающих потоки

Этот код надо выполнять 1 раз при завершении основной программы?

Да, только один раз. Самое удобное место - обработчик OnDestroy главной формы

впечатление, что он просто стоит и ничего не делает

Вызовите из потока для отладки что-нибудь внешне заметное, например хотя бы вставье после вызова IncCounter MessageBeep(0). А вообще, разве трудно поставить точку останова внутри метода Execute и посмотреть отладчиком?


 
y-soft ©   (2006-09-23 20:12) [12]

>Destroyer ©   (23.09.06 18:51) [10]

Вот, помотрите рабочий пример (кстати, вообще без использования TThread), надеюсь у вас не Windows 98? :)

http://y-soft.comsignal.ru/Delphi/Test.zip (2067 байт)


 
GrayFace ©   (2006-09-23 22:23) [13]

Боюсь, здесь хватит Application.ProcessMessages :) Просто добавь его в циклы выполняемых операций. Если это возможно, то потоки тебе не нужны.

y-soft, интересный метод.


 
Destroyer ©   (2006-09-24 14:14) [14]

GrayFace, Application.ProcessMessages тут не пойдет.

y-soft, очень хороший пример. Там используется InterlockedIncrement(TotalTaskCount); Эта фунцкция предотвращает использование одной и тойже переменной несколькими потоками. У меня в потоке очень большой код и переделывать его просто нет смысла. Значит если не переделывать, то одновременный запуск нескольких копий потока будет некорректным, т.к. переменные будут использоваться беспорядочно. Я попробовал и увидел, что некоторые функции в потоке выполняются нормально, некоторые не до конца, до некоторых дело вообще не доходит, причем нет постоянства выполнения или не выполнения той или иной функции. Видимо, это результат такого использования переменных.
Наверное в моем случае надо использовать сообщения. Т.е. идет последовательность вызовов потока.


TimerThread.Create("параметры");

TimerThread.Create("параметры");

TimerThread.Create("параметры");


И перед каждым вызовом надо что-то делать? (но ожидание здесь нельзя вставлять, например тогоже сообщения). Значит надо действовать как-то по другому. Пока я могу предложить - запоминание выполненных вызовов потока и с приходом сообщения продолжение следующего вызова. Но мне такой подход кажется не очень приятным. Может посоветуете что-нибудь?


 
y-soft ©   (2006-09-24 17:17) [15]

>Destroyer ©   (24.09.06 14:14) [14]

Не видя Вашего кода трудно ставить диагнозы


 
guav ©   (2006-09-24 18:18) [16]

Один постоянно живущий дополнительный поток с GetMessage-циклом.
Действия - сообщения, передаваемые потоку через PostThreadMessage.


 
fs_more   (2006-09-24 18:41) [17]

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

Кстати, у меня похожая задача. Я сделал очередь команд (массив записей) и 2 указателя: поз чтения и записи. В основном потоке пишу что надо сделать, а доп поток делает по мере сих ;), выбирая записи по одной. Говорят это неоптимально, т.к. можно слать сообщения (о которых я писал выше), но мне так удебно по причине работы с большими массивами данных.

Если уж так нужно дожидаться окончания отработки процесса, то нужно не писать в цикле Sleep(), а установить потоку автозавершение + ждать его завершения его собственным методом <переменная-поток>.WaitFor;

Зависание программы может быть вызвано тем, что поток после завершения не уничтожается и не сбрасывает флаг ThreadBusy. Либо сбрасывает его прежде чем самоуничтожится (тогда по факту поток не уничтожился, а осн поток думает что уже :))


 
y-soft ©   (2006-09-24 19:19) [18]

>fs_more   (24.09.06 18:41) [17]

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

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


 
GrayFace ©   (2006-09-26 19:38) [19]

Destroyer ©   (24.09.06 14:14) [14]
GrayFace, Application.ProcessMessages тут не пойдет.

Почему?

К потоковым вариантам мне как-то нечего добавить. Разве что то, что вариант y-soft на Win9x не поддерживается.



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

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

Наверх




Память: 0.51 MB
Время: 0.041 c
15-1161194808
yura32
2006-10-18 22:06
2006.11.05
SQL+PHP


2-1161365018
kcol
2006-10-20 21:23
2006.11.05
функция / процедура


2-1161342646
ceval
2006-10-20 15:10
2006.11.05
подскажите как правельно сделать


15-1160376020
zdm
2006-10-09 10:40
2006.11.05
Фискальный регистратор


15-1161178126
infom
2006-10-18 17:28
2006.11.05
Компонент для построения диаграмм.





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский