-
Не так давно я достаточно негативно отзывался о реализации класса TThread в Delphi с завлением, что потоки при завершении иногда виснут. Все-таки тогда правда была не на моей стороне. Просто потоки крутятся в различных DLL-ках, и вполне закономерно, что при выгрузке с помощью FreeLibrary библиотеки, в которой еще крутятся потоки, происходят различные неприятные вещи.
Основное правило, которого необходимо придерживаться при реализации многопоточности в DLL: библиотека обязана экспортировать функцию очистки данных. Перед выгрузкой библиотеки следует вызывать данную функцию (она сможет завершить все потоки), а уже после вызвать FreeLibrary().
Потоки невозможно уничтожить в секции finalization библиотеки. Пробовать бесполезно, могут быть лишь такие варианты: - винда срубает потоки перед выгрузкой библиотеки. В этом случае возникнет зависание при вызове TThread.Free(), т.к. будет вечно ожидаться завершение работы несуществующего потока. Данный вариант происходит при завершении работы процесса. - потоки не срубаются. Выгрузка библиотеки с работающими потоками приводит к Access Violation, процесс может завершиться. - если и EXE и DLL скомпилированы с пакетами, то выгрузка библиотеки не приводит к AV, однако и потоки не завершаются.
Дополнительно: потоки нельзя завершить в finalization DLL-ки, поскольку, выгрузка библиотеки и завершение потока - взаимоисключающие вещи. Поток можно создать в initialization библиотеки, однако Windows его не запустит, пока инициализация библиотеки не завершится (эти вещи также взаимоисключающие).
Все сказанное ни коим образом не относится к работе EXE - там таких проблем не возникает.
Описанная проблема раскрывается в хэлпе (и всплывала на тех или иных форумах), но не все с подобными явлениями сталкивались, поэтому для кого-либо данная ветка может оказаться полезной.
-
> > Loginov Dmitry © (23.04.08 23:32)
Какой ужжасс...
-
а зачем отдельная ф-ция? перед выгрузкой же автоматически вызывается дллмайн с параметром длл_процесс_детач...
-
Предлагаю в след. раз почитать Рихтера про потоки, процессы и т.д. Т.к., с одной стороны, описаны стандартные грабли новичков, с другой - и, думаю, автор уже это понимает - все они исходят из вообще непонимания принципов работы потоков в виндовс, к которым TThread является лишь оберткой. Причем обертка эта такова, что она фактически очень слабо управляет собтвенно виндовыми объектами потоков, которыми, впрочем, "мощнее" управлять и нельзя по определению. Однако легкость и податливость в управлении "с наружи" другими, "более простыми" компонентами, действительно сильно сбивает с толку при попытках так же "по простому" манипулировать экземпляром объекта TThread.
-
> описаны стандартные грабли новичков
Думаю, что и не только новичков. В той же степени это касается и профессионалов, еще не успевших по каким-либо причинам прочитать MSDN на все 100%
-
> Loginov Dmitry © (24.04.08 07:56) [4] > не успевших по каким-либо причинам > прочитать MSDN на все 100%
В данном вопросе и объеме достаточно почитать Рихтера, эта тема у него очень подробно раскрыта. Могу ошибаться, проверять не буду, но пожалуй у него легко найти ответы на все вопросы из серии "че за нафик?" по всем упомянутым граблям.
-
Пользуйтесь напрямую CreateThread - в чём проблема то :) Будете потом всё валить на WinAPI вместо TThread...
-
> Loginov Dmitry © (23.04.08 23:32)
> Потоки невозможно уничтожить в секции finalization библиотеки
Возможно. Невозможно другое - дождаться сигнала завершения потока в функции ожидания Объясняется это довольно просто - в момент финализации dll-модуля PEB текущего процесса заблокирован и система не может внести туда изменения, касаемые TIB завершившегося потока, а флаг завершения потока система может поднять только после внесения этих изменений. Т.е. в этой ситуации получается нечто вроде дедлока.
-
> Котик Б (24.04.08 09:26) [6] > Пользуйтесь напрямую CreateThread - в чём проблема то :)
Попользуйся -- узнаешь.
-
ну собственно что и требовалось доказать. Помню ту тему, где с апломбом утверждалось, что потоки в Delphi реализованы криво ;) Смешно. Просто кто-то не умеет их готовить.
Все остальное было сказано верно - читайте Рихтера, особенно про константы, передаваемые в главную функцию DLL. Никакой такой специальной функции экспортируемой библиотекой не надо, MS уже давно все продумала ;)
-
> DiamondShark © (24.04.08 15:14) [8] > Попользуйся -- узнаешь.
Суть не в том, что "узнаешь", а в том, кто виноват. :)
-
> Все остальное было сказано верно - читайте Рихтера, особенно > про константы, передаваемые в главную функцию DLL. Никакой > такой специальной функции экспортируемой библиотекой не > надо, MS уже давно все продумала ;)
Перечитал Рихтера (по части многопоточности). Но все им написанное я уже читал в Windows SDK. Т.е. есть допустим некая функция DllMain, в которую передается один их 4-х параметров. Насколько я понимаю, параметры DLL_THREAD_ATTACH и DLL_THREAD_DETACH в данной проблеме врядли чем-либо помогут. А DLL_PROCESS_ATTACH и DLL_PROCESS_DETACH это тоже, что и initialization и finalization в DLL (если я правильно понял). Ну и что же тогда "MS уже давно продумала"?
-
Что-то непонятно, вообще о чем разговор. Потоки вообще, не привязаны к какой-то конкретной билиотеке. К процессу приявязаны, если так можно выразиться.
-
Loginov Dmitry © (25.04.08 1:03) [11] Ну и что же тогда "MS уже давно продумала"?
то, что:
Loginov Dmitry © (23.04.08 23:32) потоки нельзя завершить в finalization DLL-ки, поскольку, выгрузка библиотеки и завершение потока - взаимоисключающие вещи
это ты с чего взял? Потоки, созданные в DLL - надо завершать в DLL, а потоки, созданные в другом месте - ндадо завершать в соответствующем месте.
Loginov Dmitry © (23.04.08 23:32) Поток можно создать в initialization библиотеки, однако Windows его не запустит, пока инициализация библиотеки не завершится (эти вещи также взаимоисключающие).
я не понимаю, откуда ты черпаешь информацию.
-
возможно ты не понимаешь как завершать потоки в DLL?
Вызывается FreeLibrary из приложения. Соответственно, в контексте того же потока вызывается DLL_PROCESS_DETACH (поговорим пока о нем, а не о DLL_THREAD_...), и судя по всему вызывается finalization модулей библиотеки в Delphi.
Код обработки должен быть следующий - запущенным потокам делается Terminate, после чего идет WaitFor... функции, ожидающие завершения этих потоков.
В самих потоках естественно должна быть своевременная проверка на Terminated свойство потока, и если оно выставлено - кратчайшим способом финализировать все ресурсы и удалять поток.
Это - правильная идеология работы.
-
> я не понимаю, откуда ты черпаешь информацию.
1. Столкнулся с проблемой на практике, сделал выводы 2. Подкрепил правильность выводов прочтением Windows SDK и Рихтера
> Код обработки должен быть следующий - запущенным потокам > делается Terminate, после чего идет WaitFor... функции, > ожидающие завершения этих потоков. > > В самих потоках естественно должна быть своевременная проверка > на Terminated свойство потока, и если оно выставлено - кратчайшим > способом финализировать все ресурсы и удалять поток. > > Это - правильная идеология работы.
Но эта идеология не работает! Об этом в [0] как раз и идет разговор ;)
-
> это ты с чего взял? Потоки, созданные в DLL - надо завершать > в DLL, а потоки, созданные в другом месте - ндадо завершать > в соответствующем месте.
Какая разница, где завершать потоки (потоки Windows, а не Delphiйские TThread) ?
-
Примечательно, что уничтожение потока нормально отрабатывает при его создании функцией CreateThread. А AV при выгрузке сыплются именно из-за BeginThread. Эта функция вызывает CreateThread, но передает в нее вместо вашей функции некую функцию ThreadWrapper. Видимо, связано именно с этим...
-
> Loginov Dmitry © (25.04.08 10:42) [17]
Проиллюстрируй в коде ..
-
> Проиллюстрируй в коде ..
unit DllUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes;
implementation
var
Term: Boolean = False;
function MyThreadFunc(Param: Cardinal): Integer; stdcall;
begin
Result := 0;
while not Term do
Sleep(100);
Term := False;
end;
procedure StartMythread;
var
I: Cardinal;
begin
I := 0;
BeginThread(nil, 0, @MyThreadFunc, 0, 0, I); end;
initialization
StartMythread;
finalization
Term := True;
while Term do
Sleep(0);
Sleep(0); end.
DLL и ЕХЕ скомпилированы без пакетов. В ЕХЕ две кнопки: с LoadLibrary и FreeLibrary
-
> // Ошибок нет
"Ты суслика видишь ? И я не вижу. А он есть !" (с)
> // Ожидаем завершения потока
Это вовсе не ожидание завершения потока, а всего лишь ожидание момента когда переменная Term примет значение False.
> Sleep(0); // AV валятся здесь
Именно здесь AV валиться не может, не выдумывай. Неоткуда тут AV взяться.
-
> BeginThread(nil, 0, @MyThreadFunc, 0, 0, I); // Ошибка есть
Ошибка в твоём коде.
-
> Именно здесь AV валиться не может, не выдумывай. > Неоткуда тут AV взяться.
Я ведь когда-то тоже так думал! А оказывается, что есть откуда. И именно на Sleep(0)...
-
> Ошибка в твоём коде.
Где именно?
-
> BeginThread(nil, 0, @MyThreadFunc, 0, 0, I); // Ошибка есть
При объявлении MyThreadFunc ты указал соглашение stdcall - вот граблями и получил)
-
Смотри что передаёшь в BeginThread и что она требует.
-
Кстати, если писать такое без @, то компилятор будет проверять прототип.
-
> именно на Sleep(0)
То, что остановившись на этой строчке по брейкпойнту ты жмакнул F7 и получил AV, вовсе не означает, что именно эта строчка является причиной исключения.
-
Мда... Неудачный получился пример. Сорри за невнимательность :( Но один фик с TThread AV гарантированно лезут...
-
> один фик с TThread AV гарантированно лезут
Иллюстрируй в коде теперь с TThread..
-
> Иллюстрируй в коде теперь с TThread..
unit DllUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes;
type
TMyThread = class(TThread)
protected
procedure Execute; override;
end;
implementation
var
MyThread: TMyThread;
procedure StartMythread;
begin
MyThread := TMyThread.Create(False);
end;
procedure TMyThread.Execute;
begin
inherited;
while not Terminated do
Sleep(50);
end;
initialization
StartMythread;
finalization
end.
Опять ведь найдете че-нибудь.... :)
-
> Loginov Dmitry © (25.04.08 11:55) [30]
1,4 - ничего удивительного, абсолютно ожидаемый результат. 2,3 - см. [7] на предмет причины зависания
-
> 1,4 - ничего удивительного, абсолютно ожидаемый результат. > > 2,3 - см. [7] на предмет причины зависания
Да насчет зависаний как раз-то все и понятно. Windows SDK об этом говорит вполне четко.
Таким образом получается, что в любом случае мы ловим или AV, или зависание. А можно ли это как-то обойти без дополнительной экспортируемой функции? И как?
-
> Loginov Dmitry © (25.04.08 12:13) [32] > >
Обойти в принципе можно, но реализация обхода будет уже из разряда трюков. А трюки, как известно, штука рискованая и ненадежная)
-
кстати, в finalization такой трюк обычно спасает:
MyThread.Terminate;
while Assigned(MyThread) do
Sleep(20);
Но не всегда. В случае, когда FreeLibrary библиотеке не делают, при завершении работы процесса все-равно вызывается для всех DLL finalization, но к этому моменту винда уже успевает срубить все потоки, поэтому данный код приводит лишь к зависанию.
-
> такой трюк обычно спасает
С какого перепугу ?
-
Игорь Шевченко © (25.04.08 9:42) [16] Какая разница, где завершать потоки (потоки Windows, а не Delphiйские TThread) ?
а зачем в дельфи лишать себя такого удобного класса, как TThread?
-
> С какого перепугу ?
В конце TMyThread.Execute (или в деструкторе) выполняется присвоение MyThread := nil
-
Пробегал2... (25.04.08 13:40) [36]
Тут в ветке где-то про чтение Рихтера говорили. Нафига читать Рихтера, если он в Delphi ни ухом ни рылом ?
-
> Loginov Dmitry © (25.04.08 13:40) [37]
> В конце TMyThread.Execute (или в деструкторе) выполняется > присвоение MyThread := nil >
И что ?
Появление какой-то там nil в какой-то там переменной не есть факт завершения поточной функции.
Это те же фаберже, чт о и в [19], только вид сбоку)
Ты, видимо, совершенно не понимаешь, что является главным признаком фактического завершения исполнения поточной функции.
-
Loginov Dmitry © (25.04.08 13:40) [37] В конце TMyThread.Execute (или в деструкторе) выполняется присвоение MyThread := nil
о боже. Ты вообще понимаешь что делаешь? Блин, и кто-то говорил о профессионализме... В методах классах обращаться к конкретному экземпляру класса?
Игорь Шевченко © (25.04.08 14:01) [38] Тут в ветке где-то про чтение Рихтера говорили. Нафига читать Рихтера, если он в Delphi ни ухом ни рылом ?
я пожалуй даже отвечать не буду.
-
> что является главным признаком фактического завершения исполнения > поточной функции.
Возможно возможно...
Что же по Вашему является "главным признаком фактического завершения исполнения поточной функции", и как это "что" использовать в данной ситуации?
-
> о боже. Ты вообще понимаешь что делаешь? Блин, и кто-то > говорил о профессионализме... В методах классах обращаться > к конкретному экземпляру класса?
Вот и объясни мне, как здесь можно выкрутиться без подобных извращений!
-
> Loginov Dmitry © (25.04.08 15:29) [41]
Признаком фактического завершения поточной функции является либо вызов ф-ции ExitThread() либо вызов машинструкции RET верхнего уровня вложенности.
-
Loginov Dmitry © (25.04.08 15:30) [42] Вот и объясни мне, как здесь можно выкрутиться без подобных извращений!тебе что нужно? Завершить поток? Так сделай ему Terminate, поток должен своевременно проверять Terminated свойство и выходить из Execute когда оно true Если ты сделаешь FreeOnTerminate := true - это значит, что после выхода из Execute поток сам вызовет Destroy, то есть такому потоку-классу НЕ НУЖНО делать Free / Destroy. Если тебе нужно дождаться завершения работы потока - смотри функции WaitForSingleObject / WaitForMultipleObjects, или уже реализованный метод TThread.WaitFor Поэтому если смотреть твой пример [30], то при классе со свойством: FreeOnTerminate := true - правильный сценарий 3. При FreeOnTerminate := false правилен такой сценарий: MyThread.Terminate;
MyThread.WaitFor;
MyThread.Free ; Косяк может быть действительно в том, когда вызывается код из секции finalization. Я в библиотеках им не пользуюсь. Используй стандартные посылки Windows. То есть: library DllEntry;
procedure DLLEntryPoint(Reason: DWORD);
begin
case Reason:
Dll_Process_Attach: ДЕЛАЕМ ИНИЦИАЛИЗАЦИЮ БИБЛИОТЕКИ
DLL_PROCESS_DETACH: ДЕЛАЕМ ФИНАЛИЗАЦИЮ ВСЕХ ДАННЫХ БИБЛИОТЕКИ
end;
end;
begin
if DllProc = nil then
begin
DllProc := @DLLEntryPoint;
DllEntryPoint(Dll_Process_Attach);
end;
end.
-
> Пробегал2
> Косяк может быть действительно в том, когда вызывается код > из секции finalization. Я в библиотеках им не пользуюсь
Пользоваться или не пользоваться - это твое право. Но косяк с дедлоком будет в любом случае.
-
> Пробегал2... (25.04.08 15:59) [44]
> если смотреть твой пример [30], то при классе со свойством: > FreeOnTerminate := true - правильный сценарий 3
Неправильный он, как и все остальные сценарии.
-
Сергей М. © (25.04.08 16:23) [45] Но косяк с дедлоком будет в любом случае
где ты там видишь дедлок?! Словами можешь описать?
-
> Признаком фактического завершения поточной функции является > либо вызов ф-ции ExitThread() либо вызов машинструкции RET > верхнего уровня вложенности.
Вызов <> признаку!
> тебе что нужно? Завершить поток? Так сделай ему Terminate, > поток должен своевременно проверять Terminated свойство > и выходить из Execute когда оно true > > Если ты сделаешь FreeOnTerminate := true - это значит, что > после выхода из Execute поток сам вызовет Destroy, то есть > такому потоку-классу НЕ НУЖНО делать Free / Destroy. > > Если тебе нужно дождаться завершения работы потока - смотри > функции WaitForSingleObject / WaitForMultipleObjects, или > уже реализованный метод TThread.WaitFor > > Поэтому если смотреть твой пример [30], то при классе со > свойством: FreeOnTerminate := true - правильный сценарий > 3.
фигня какая-то получается. Я с [0] поста доказываю, что в finalization DLL-библиотеки все это не работает, а вы все-равно внушаете, что так правильно. Да, так правильно! Но не работает!
Все-таки, я все больше прихожу к выводу, что нужна дополнительная экспортируемая функция. С вариантом, когда Винда при выгрузке DLL срубает потоки еще до finalization, достаточно сложно что-либо другое придумать. Ведь такое обращения Windows с потоками - опасно и чревато потерей данных...
-
> Словами можешь описать?
На меня снижа шло озарение и я понял в чем главная проблема нашего мира. Мы не слышам друг друга...
PS [7]
-
> Пробегал2... (25.04.08 16:33) [47]
см. [7], там как раз объяснение "словами".
-
> Loginov Dmitry © (25.04.08 16:35) [48]
> Вызов <> признаку!
Поправлюсь - не сам вызов, а результат его исполнения.
В результате исполнения вызова ExitThread или возврата по RET управление будет передано в kernel32 и никогда более не будет передано коду поточной функции, что дает все основания для беспроблемной деаллокации участка ВАП, в котором размещался машкод поточной ф-ции.
-
В дополнение к [51] - подразумевается, что код поточной ф-ции м.б. смело удален из ВАП процесса не ранее чем завершат свое выполнение все потоки, созданные ранее с указанием этой поточной функции.
-
Loginov Dmitry © (25.04.08 16:35) [48] Я с [0] поста доказываю, что в finalization DLL-библиотеки все это не работает, а вы все-равно внушаете, что так правильно
как выяснили, в finalization это действительно не работает. Поэтому не надо этот код писать в finalization. ГДЕ НУЖНО ПИСАТЬ этот код - я написал в посте [44]
jack128_ (25.04.08 16:41) [49] На меня снижа шло озарение и я понял в чем главная проблема нашего мира. Мы не слышам друг друга...
PS [7]
теперь понял! Честное слово, почему-то пропустил пост [7], не читал его... Да, наверное так и есть.
-
> Пробегал2... (25.04.08 17:14) [53]
> не надо этот код писать в finalization. ГДЕ НУЖНО ПИСАТЬ > этот код - я написал в посте [44]
Финализация юнитов вызвается в контексте вызванной DllMain(DLL_PROCESS_DETACH), так что без разницы, где будет вызвана wait-функция ожидания завершения потока - хоть непосредственно в DllMain, хоть в finalization. Результат будет один и тот же - "не дождетесь !")
-
> Финализация юнитов вызвается в контексте вызванной DllMain(DLL_PROCESS_DETACH), > так что без разницы, где будет вызвана wait-функция ожидания > завершения потока - хоть непосредственно в DllMain, хоть > в finalization. Результат будет один и тот же - "не дождетесь > !")
И это правда! Проверено! :)
-
Сергей М. © (25.04.08 17:20) [54]странно... даже как-то бредово... А где же тогда рекомендуется завершать потоки, созданные в DLL? Ведь идеологически при DLL_PROCESS_DETACH самое место... Ну тогда я вижу один извращенный вариант, других по-моему просто нет. TMyThread = class(TThread)
private
FFinallyEvent: Thandle ;
protected
procedure execute; override;
constructor Create;
destructor Destroy;
property FinallyEvent: Thandle read FFinallyEvent;
end;
....
constructor TMyThread.Create;
begin
FFinallyEvent := CreateEvent(bla bla не помню что там надо) ;
FreeOnTerminate := true ;
ResetEvent(FFinallyEvent);
inherited Create(false) ;
end;
destructor TMyThread.Destroy;
begin
CloseHandle(FFinallyEvent);
inherited;
end;
procedure TMyThread.Execute;
begin
while not terminate
РАБОТАЕМ
SetEvent(FFinallyEvent) ;
end; Соответственно, вызовы приблизительно такие: var
MyThread: TMyThread;
...
initialization
MyThread := TMyThread.Create;
finalization
MyThread.Terminate;
WaitForSingleObject(MyThread.FinallyEvent) ;
end. впрочем, как я понимаю, код с FFinallyEvent можно и убрать. Все равно событие выставляется ДО того, как фактически поток будет завершен, просто максимально приближает ожидания момента к реальному моменту уничтожения потока... Так что можно и без event'а... Все равно судя по всему FreeLibary вернет управление, но поток будет продолжать крутиться и ВАП не будет очищено, то есть вс в порядке, в результате все равно все финализируется и беспокоится нечего. event можно использовать только как знак, что поток уже на финальной стадии завершения, то есть он уже не будет ничего никуда передавать, не будет ничего создавать и открывать и прочее...
-
> Пробегал2... (25.04.08 17:52) [56]
Практически тот же изврат, что и при while Assigned(MyThread) do... но более правильный. От срубания потоков виндой все-равно не спасет! :)
-
Loginov Dmitry © (25.04.08 18:06) [57] Практически тот же изврат, что и при while Assigned(MyThread) do... но более правильный
ну нифига... У тебя все расчитано, что максимум будет создан один поток.. Второй поток уже внесет несусветную суматоху. У меня же поток по всем правилам ООП написан ;) Поменяются тольуо функции ожидания для нескольких потоков, будет использована WaitForMultipleObjects
Loginov Dmitry © (25.04.08 18:06) [57] От срубания потоков виндой все-равно не спасет! :)
да с чего ты взял, что винда потоки срубает?!?! Винда финализирует потоки не при FreeLibrary, а при завершении процесса, тогда она все ресурсы пытается освободить, созданные этим процессом.
-
> странно... даже как-то бредово... А где же тогда рекомендуется > завершать потоки, созданные в DLL? Ведь идеологически при > DLL_PROCESS_DETACH самое место...
потоки не создаются в DLL. Потоки создаются в процессе. Познай эту истину и будет тебе рулез вечный и немеряный
-
Игорь Шевченко © (25.04.08 19:05) [59] потоки не создаются в DLL. Потоки создаются в процессе
а вам доставляет удовольствие цепляться к словам? Любопытно посто.
Если действительно непонятна фраза "потоки созданные в DLL" - я уточню. Это потоки, которые созданы в том месте маш. кода, который был загружен в ВАП процесса при проецировании образа DLL.
К чему еще изволите придраться?
-
Пробегал2... (25.04.08 19:18) [60]
И чем они отличаются от потоков, созданных в другом месте кода ?
-
> Пробегал2... (25.04.08 19:18) [60]
> Это потоки, которые созданы в том месте маш. кода, который > был загружен в ВАП процесса при проецировании образа DLL
Не нало месить кислое и фиолетовое.
-
Игорь Шевченко © (25.04.08 19:23) [61] И чем они отличаются от потоков, созданных в другом месте кода ?
тем, что потоки созданные в DLL должны уничтожаться также в DLL. Это логичная методика работы.
DLL как черный ящик, она экспортирует нужный мне функционал, а как она его реализовывает - мне все равно. И то что создано в DLL - внутри нее же и должно быть финализировано.
С таким же успехом и глобальные переменные, выделенные в DLL - можно очищать в самом приложении, не так ли? Только это нелогично, это - неудобно.
Плюс весь WinApi спроектирован таким образом и я думаю стоит придерживаться этого подхода. Когда вам требуется принять некую структуру данных - ВЫ выделяете память под нее и передаете на нее ссылку, по которой эта структура заполняется библиотекой. Заметьте, не библиотека выделяет память и передает ссылку на заполненную структуру, которую ВЫ потом должны уничтожить. Принцип один: если намусорил - подчисти за СОБОЙ.
И если там какая-то DLL создала поток для реализации функционала - она же этот поток и должна уничтожить. Имхо, если DLL хорошо спроектирована - то я в любое время могу вызывать FreeLibrary - и DLL должна очистить все ресурсы, занятые ею в процессе работы и вернуть все в то состояние, которое было до вызова LoadLibrary.
-
Пробегал2... (25.04.08 20:35) [63]
Это всего лишь твое имхо. Ни больше, ни меньше. В системе и в приложениях есть примеры и одного подхода, и другого, не надо выдавать свое имхо за абсолютную истину, а главное, делать из этого имха какие-то глобальные выводы.
-
Удалено модератором
-
Здесь http://msdn2.microsoft.com/en-us/library/ms885202.aspxчерным по белому нарисована логика вызовов DllMain с тем или иным резоном. С учетом же того, что вызовы DllMain конкретного модуля в контексте текущего процесса осуществляются системой синхронно, "феномен зависания" становится совершенно очевидным и понятным, даже без вникания во всякие там PEBы и TIBы.
-
))
-
> ну нифига... У тебя все расчитано, что максимум будет создан > один поток.. Второй поток уже внесет несусветную суматоху.
мне просто не требуется, чтобы в программе крутилось несколько однотипных потоков, поэтому выбрано такое решение. В других случаях будет выбрано другое решение. Кстати, после WaitForSingleObject() стоило бы выждать хотябы немного времени (например 50 / 100 мс), чтобы стать более уверенным, что функция потока уперлась в ExitThread() (а этот код уже выполняется не в нашей DLL-ке, а в kernel) и ожидает снятия блокировки, в этом случае выгрузка библиотеки уже точно ни на что не повлияет, и никаких AV не будет.
> да с чего ты взял, что винда потоки срубает?!?! Винда финализирует > потоки не при FreeLibrary
Вот срубает и все тут. Причем FreeLibrary() перед закрытием программы вызывается. В одном случае срубает, в другом - не срубает. Есть конечно подозрение, что где-то в системе лишний раз LoadLibrary() для моей библиотеки кто-то вызвал, но наврядли... Проверю на следующей неделе.
> И чем они отличаются от потоков, созданных в другом месте > кода ?
Потоковой функцией, код которой выгружается вместе с DLL-кой в том случае, если он принадлежит DLL-ке.
-
Игорь Шевченко © (25.04.08 20:42) [64] Это всего лишь твое имхо. Ни больше, ни меньше
ок, давайте по другому. Игорь, только честно - у вас были проекты, где поток создавался в коде DLL, а завершался в другом месте?
P.S. Это я уже не говорю об использовании класса TThread, там уж как ни крути финализировать надо там же, где создаешь.
-
Пробегал2... (25.04.08 21:42) [69]
Я не единственный разработчик программного обеспечения на этом свете. И на слабо бери кого-нибудь другого.
-
Игорь Шевченко © (25.04.08 22:50) [70]
как я понимаю, ответ нет, ни в одном проекте вы так не делали. Что и требовалось доказать.
-
Пробегал2... (25.04.08 23:40) [71]
И на слабо бери кого-нибудь другого. Доказатель фигов
-
ну хорошо. Я моих проектах никогда не было, чтобы поток создавался в DLL, а завершался в другой DLL или в самом приложении. И мне такой подход кажется нелогичным и путанным, когда DLL обязывает выполнять какие-то телодвижения кроме загрузки и выгрузки библиотеки.
Да, это мое ИМХО. ну не согласны - и не согласны ;)
-
Пробегал2... (26.04.08 02:52) [73]
С точки зрения процесса, и основное приложения и DLL - это всего лишь части кода, связанные в единое адресное пространство (про различные менеджеры памяти или VMT для дельфийских классов в этом аксепте упоминать сейчас не будем, так как консепция потоков не зависит от используемого языка программирования). Таким образом, ничто не мешает потоку, начавшись в одной части кода. завершится абсолютно в другой. Система это делать позволяет. Более того, система даже позволяет создать поток в одном приложении, а завершать его в другом, используя функции CreateRemoteThread и TerminateThread
Давайте все-таки различать - возможно/невозможно и удобно/неудобно
-
Игорь, я Вами восхищаюсь :)
-
Удалено модератором
-
Кто-нибудь может привести пример(свой, чужой - неважно), где бы в DLL создавался поток (BeginThread/TThread), затем DLL из приложения выгружалась бы, а поток продолжал работать?
-
> где бы в DLL создавался поток (BeginThread/TThread), затем > DLL из приложения выгружалась бы, а поток продолжал работать?
На ум приходит только одно: потоковая функция расположена в другом модуле.
-
> > где бы в DLL создавался поток (BeginThread/TThread), затем > > DLL из приложения выгружалась бы, а поток продолжал работать? > На ум приходит только одно: потоковая функция расположена > в другом модуле.
Потоковая это только адрес начала потока, далее может быть переходы куда угодно. Легко представить себе начало потока в dll, затем продолжение в другой и вызов ExitThread уже оттуда. Можно даже и без ExitThread, обычным возвратом, просто переход в другой модуль не по call, а по jmp. Или так: в потоке создаются фиберы, и поток завершается в фибере созданном в другом модуле.
-
> На ум приходит только одно: потоковая функция расположена > в другом модуле.
т.е.?
-
Игорь Шевченко © (26.04.08 11:02) [74] Давайте все-таки различать - возможно/невозможно и удобно/неудобно
а процитируйте хоть одно мое слово, где я говорю, что это невозможно?
На протяжении всей ветки я говорил, что это НЕПРАВИЛЬНО, а не невозможно. С точки зрения логики путанно. Поэтому в моих проектах и я уверен в ваших такого нету.
И не понимаю почему вы начинаете спорить, а потом зачем-то говорите про различие слов невозможно и неудобно, очевидно с самого начала говорили про неудобно.
-
Пробегал2... (27.04.08 12:32) [81]
> На протяжении всей ветки я говорил, что это НЕПРАВИЛЬНО, > а не невозможно
Что есть НЕПРАВИЛЬНО ? Ты, прежде чем претендовать на абсолютную истину, определись с терминологией. А то как в детском саду - "а я имел в виду другое!"
-
Игорь Шевченко © (27.04.08 12:35) [82] Что есть НЕПРАВИЛЬНО ?
ну в принципе конечно можно понять неправильно с той точки зрения, что работать не будет правильно.
Я имел в виду неправильно с той точки зрения, что так делать не принято, то есть плохой подход, хоть и может работать.
-
Пробегал2... (27.04.08 13:00) [83]
Насчет "работать не будет правильно" - если система позволяет, почему не будет работать ?
> Я имел в виду неправильно с той точки зрения, что так делать > не принято, то есть плохой подход
А если не секрет, где есть свод правил, где однозначно написано, как принято делать, как не принято, что есть плохой подход, что есть хороший подход ? То есть, я понимаю, что Дейкстра сказал "goto considered harmful" и это занесено на скрижали. Так вот, есть такие же скрижали относительно потоков в DLL ?
Я до сих пор считал, если система позволяет нечто сделать, то вне зависимости от личных приверженностей, это нечто имеет право на существование.
-
> Экс-Оригинал (26.04.08 22:53) [80]
> т.е.?
Что не понятно ?
-
> Что не понятно ?
Непонятно, что имеется ввиду. Что за другой модуль?
-
> Что за другой модуль?
Любой PE-модуль в ВАП текущего процесса, содержащий исполняемый код поточной функции, адрес которой был указан при вызове CreateThread.
|