Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Почему поток обрывается?


Автор: Dota 24.7.2010, 19:36
Почему поток обрывается при закрытии главного окна и завершается приложение?
Блин, где не читаю, везде пишут, что процесс завершится только тогда, когда завершится последний поток. А тут получается что то принудительно его завершает.
Ситуация усложняется еще и тем, что потоки собираюсь создавать в DLL и программа, которая будет использовать DLL точно также может в любой момент обрываться и обрывается поток...


Код

function TestThread(Parameter: Pointer): Integer;
var
  I: Integer;
begin
  for I := 1 to 10 do
  begin
    Form1.Edit1.Text := IntToStr(I);
    Sleep(1000);
  end;

  EndThread(0);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  T: LongWord;
begin
  CloseHandle(BeginThread(nil, 0, TestThread, nil, 0, T));
end;

Автор: CodeMonkey 24.7.2010, 21:09
Потому что "end." в dpr файле - это вызов Halt(0).

Автор: xkor 24.7.2010, 22:45
CodeMonkey, и это надо сказать вполне логично учитывая что на "end." ещё и finalization у всех модулей вызывается
Dota, прими как данность что в обычном дельфи-приложении основной поток должен умирать последним
Цитата(Dota @  24.7.2010,  19:36 Найти цитируемый пост)
Ситуация усложняется еще и тем, что потоки собираюсь создавать в DLL и программа, которая будет использовать DLL точно также может в любой момент обрываться и обрывается поток...

При завершении программы вроде должна вызываться основная функция длл (DllProc вроде обзывается в дельфи и её ещё надо обозначить иначе стандартная заглушка только finalization модулей вызовет, хотя можно и через них) с параметром что длл выгружается, вот в этот момент можно послать всем своим потокам сигнал что надо срочно завершаться и дождаться их завершения
Хотя не уверен что эта функция вызывается если программа завершается по Halt

Автор: Dota 25.7.2010, 21:25
Спасибо. Теперь ясно, что не кошерно все устроено в этом плане.

Автор: CodeMonkey 25.7.2010, 23:45
Цитата(Dota @  25.7.2010,  22:25 Найти цитируемый пост)
Теперь ясно, что не кошерно все устроено в этом плане. 

Пффф. Будьте осторожны: ваши желания могут исполнится. И тогда вам это не понравится. Подумайте сами почему. xkor уже дал подсказку.

Цитата(xkor @  24.7.2010,  23:45 Найти цитируемый пост)
вот в этот момент можно послать всем своим потокам сигнал что надо срочно завершаться и дождаться их завершения

И с неслабым шансом получим deadlock, потому что завершение потока приводит к вызову DllMain, которая блокирована главным потоком.

Потоки должны закрыться до того, как DLL начнёт процесс выгрузки.

Автор: xkor 26.7.2010, 00:15
Цитата(CodeMonkey @  25.7.2010,  23:45 Найти цитируемый пост)
И с неслабым шансом получим deadlock, потому что завершение потока приводит к вызову DllMain, которая блокирована главным потоком.

блин точно, как то не подумал об этом...
Цитата(CodeMonkey @  25.7.2010,  23:45 Найти цитируемый пост)
Потоки должны закрыться до того, как DLL начнёт процесс выгрузки. 

и как тогда потоки должны узнавать о том что процессу вот вот настанет конец?

Автор: CodeMonkey 26.7.2010, 11:16
Цитата(xkor @  26.7.2010,  01:15 Найти цитируемый пост)
и как тогда потоки должны узнавать о том что процессу вот вот настанет конец? 


Очевидно, что протокол DLL должен предусматривать функцию Done (и Init).

Часто народ думает: во, у нас же есть DllMain! Давайте сюда поместим инициализацию и очистку ресурсов (в нашем случае: остановку потоков). Но они не осознают, что http://www.transl-gunsmoker.ru/2009/01/dllmain.html.

К примеру, пакеты Delphi так и устроены: несмотря на то, что это просто DLL, их инициализация и финализация выброшены в отдельные функции, а не выполняется в рамках DllMain.

Автор: Dota 27.7.2010, 19:43
Я пока придумал решение - использовать критические секции в DLLMain. Поток в начале работы входит в критическую секцию. И DLLMain тоже входит. Пока поток не покинет критическую секцию DLLMain будет ждать...

Автор: CodeMonkey 28.7.2010, 11:34
Цитата(Dota @  27.7.2010,  20:43 Найти цитируемый пост)
Поток в начале работы входит в критическую секцию. И DLLMain тоже входит. Пока поток не покинет критическую секцию DLLMain будет ждать... 


Поздравляю, ваши шансы на deadlock только что удвоились. Если раньше у вас была только блокировка загрузчика, то теперь у вас есть ещё и критическая секция.

Автор: Dota 28.7.2010, 12:50
Но ведь критическая секция работает, если верить Рихтеру на пользовательском уровне, оперируя атомарными операциями...

Есть ли способ доработать потоку правильными методами?

Автор: CodeMonkey 28.7.2010, 13:43
Цитата(Dota @  28.7.2010,  13:50 Найти цитируемый пост)
Но ведь критическая секция работает, если верить Рихтеру на пользовательском уровне, оперируя атомарными операциями...


А причём тут это? Что, по-вашему произойдёт, если один поток войдёт в критическую секцию (или в любой другой объект синхронизации) и будет ждать там другой поток, которому тоже нужно туда войти? Deadlock и получится.

Вы, кажется, не поняли, о чём идёт речь. Попробуйте вдумчиво прочитать того же Рихтера, где он говорит про DllMain, или http://www.transl-gunsmoker.ru/2009/01/dllmain.html.

Автор: Dota 28.7.2010, 19:49
Никак не могу прикинуть, где деадлок может случиться.

Имеем 2 и более теоретически возможно возникающих потока в DLL.

Запускаются потоки. Входим в критическую секцию. Внезапно завершается приложение, вызывается DLLMain с параметром DLL_PROCESS_DETACH. Входим в эту же критическую секцию. Ждем пока нам не отдадут управление над критической секцией. Завершаем DLLMain _не выходя из критической секции_, тоесть все потоки которые не начали работу, ожидая своей очереди, автоматически умрут с приложением.

Автор: Dota 28.7.2010, 21:00
Попробывал использовать критическую секцию в DLLMain - фиг там, не пашет, сразу пропускает.
Хотя сколько коду видел и сколько валяется на теренах интернета, где пользуются критическими секциями в DLLMain ничего не подозревая. Даже в официальных примерах MSDN, если память не изменяет!

Автор: bems 28.7.2010, 23:43
Цитата(Dota @  28.7.2010,  12:50 Найти цитируемый пост)
Но ведь критическая секция работает, если верить Рихтеру на пользовательском уровне, оперируя атомарными операциями...
Нет, она _может_ отработать не заходя в кернелмод, если сразу не занята, или освобождается, пока не вытекет счетчик. А потом задействуется объект ядра.

Автор: Dota 29.7.2010, 10:39
Вообщем ситуация такая. ExitProcess используемый всеми программами для корректного завершения убивает все остальные потоки, кроме того, с которого вызван ExitProcess еще до того, как вызывается DLLMain PROCESS_DETACH  smile 

Единственное решение, еще написанное Рихтером - секас с перехватом ExitProcess.

Автор: Dota 29.7.2010, 11:13
Решение найдено. Использование критической секции совместно с потоком в DLL_THREAD_DETACH. Пока поток не завершится, DLL_THREAD_DETACH будет ждать его завершения, вот и все, никакие ресурсы на стадии остановки потоков ясное дело не освобождаются, потому можно смело там юзать критические секции. Деадлок при грамотном программировании тоже малореален, если вообще реален.

Автор: Dota 29.7.2010, 14:11
Млин. Это не выход, приложение может подвиснуть на время, при убиении какого то своего потока, пока не выполнится код в моем потоке. Я в шоке. Перерыл весь интернет и так и не нашел ответа, как дать возможность потоку доработать до конца...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)