Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.06.08;
Скачать: CL | DM;

Вниз

Многопоточность и Delphi   Найти похожие ветки 

 
Пробегал2...   (2008-04-25 15:26) [40]

Loginov Dmitry ©   (25.04.08 13:40) [37]
В конце TMyThread.Execute (или в деструкторе) выполняется присвоение MyThread := nil


о боже. Ты вообще понимаешь что делаешь? Блин, и кто-то говорил о профессионализме... В методах классах обращаться к конкретному экземпляру класса?

Игорь Шевченко ©   (25.04.08 14:01) [38]
Тут в ветке где-то про чтение Рихтера говорили. Нафига читать Рихтера, если он в Delphi ни ухом ни рылом ?


я пожалуй даже отвечать не буду.


 
Loginov Dmitry ©   (2008-04-25 15:29) [41]


> что является главным признаком фактического завершения исполнения
> поточной функции.


Возможно возможно...

Что же по Вашему является "главным признаком фактического завершения исполнения поточной функции", и как это "что" использовать в данной ситуации?


 
Loginov Dmitry ©   (2008-04-25 15:30) [42]


> о боже. Ты вообще понимаешь что делаешь? Блин, и кто-то
> говорил о профессионализме... В методах классах обращаться
> к конкретному экземпляру класса?


Вот и объясни мне, как здесь можно выкрутиться без подобных извращений!


 
Сергей М. ©   (2008-04-25 15:45) [43]


> Loginov Dmitry ©   (25.04.08 15:29) [41]


Признаком фактического завершения поточной функции является либо вызов ф-ции ExitThread() либо вызов машинструкции RET верхнего уровня вложенности.


 
Пробегал2...   (2008-04-25 15:59) [44]

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.


 
Сергей М. ©   (2008-04-25 16:23) [45]


> Пробегал2


> Косяк может быть действительно в том, когда вызывается код
> из секции finalization. Я в библиотеках им не пользуюсь


Пользоваться или не пользоваться - это твое право.
Но косяк с дедлоком будет в любом случае.


 
Сергей М. ©   (2008-04-25 16:25) [46]


> Пробегал2...   (25.04.08 15:59) [44]


> если смотреть твой пример [30], то при классе со свойством:
>  FreeOnTerminate := true - правильный сценарий 3


Неправильный он, как и все остальные сценарии.


 
Пробегал2...   (2008-04-25 16:33) [47]

Сергей М. ©   (25.04.08 16:23) [45]
Но косяк с дедлоком будет в любом случае


где ты там видишь дедлок?! Словами можешь описать?


 
Loginov Dmitry ©   (2008-04-25 16:35) [48]


> Признаком фактического завершения поточной функции является
> либо вызов ф-ции 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 с потоками - опасно и чревато потерей данных...


 
jack128_   (2008-04-25 16:41) [49]


> Словами можешь описать?

На меня снижа шло озарение и я понял в чем главная проблема нашего мира.  Мы не слышам друг друга...  

PS
[7]


 
Сергей М. ©   (2008-04-25 16:42) [50]


> Пробегал2...   (25.04.08 16:33) [47]


см. [7], там как раз объяснение "словами".


 
Сергей М. ©   (2008-04-25 16:53) [51]


> Loginov Dmitry ©   (25.04.08 16:35) [48]


> Вызов <> признаку!


Поправлюсь - не сам вызов, а результат его исполнения.

В результате исполнения вызова ExitThread или возврата по RET управление будет передано в kernel32 и никогда более не будет передано коду поточной функции, что дает все основания для беспроблемной деаллокации участка ВАП, в котором размещался машкод поточной ф-ции.


 
Сергей М. ©   (2008-04-25 17:04) [52]

В дополнение к [51] - подразумевается, что код поточной ф-ции м.б. смело удален из ВАП процесса не ранее чем завершат свое выполнение все потоки, созданные ранее с указанием этой поточной функции.


 
Пробегал2...   (2008-04-25 17:14) [53]

Loginov Dmitry ©   (25.04.08 16:35) [48]
Я с [0] поста доказываю, что в finalization DLL-библиотеки все это не работает, а вы все-равно внушаете, что так правильно


как выяснили, в finalization это действительно не работает. Поэтому не надо этот код писать в finalization. ГДЕ НУЖНО ПИСАТЬ этот код - я написал в посте [44]

jack128_   (25.04.08 16:41) [49]
На меня снижа шло озарение и я понял в чем главная проблема нашего мира.  Мы не слышам друг друга...  

PS
[7]


теперь понял! Честное слово, почему-то пропустил пост [7], не читал его... Да, наверное так и есть.


 
Сергей М. ©   (2008-04-25 17:20) [54]


> Пробегал2...   (25.04.08 17:14) [53]


> не надо этот код писать в finalization. ГДЕ НУЖНО ПИСАТЬ
> этот код - я написал в посте [44]


Финализация юнитов вызвается в контексте вызванной DllMain(DLL_PROCESS_DETACH), так что без разницы, где будет вызвана wait-функция ожидания завершения потока - хоть непосредственно в DllMain, хоть в finalization. Результат будет один и тот же - "не дождетесь !")


 
Loginov Dmitry ©   (2008-04-25 17:29) [55]


> Финализация юнитов вызвается в контексте вызванной DllMain(DLL_PROCESS_DETACH),
>  так что без разницы, где будет вызвана wait-функция ожидания
> завершения потока - хоть непосредственно в DllMain, хоть
> в finalization. Результат будет один и тот же - "не дождетесь
> !")


И это правда!
Проверено! :)


 
Пробегал2...   (2008-04-25 17:52) [56]

Сергей М. ©   (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 можно использовать только как знак, что поток уже на финальной стадии завершения, то есть он уже не будет ничего никуда передавать, не будет ничего создавать и открывать и прочее...


 
Loginov Dmitry ©   (2008-04-25 18:06) [57]


> Пробегал2...   (25.04.08 17:52) [56]


Практически тот же изврат, что и при while Assigned(MyThread) do...
но более правильный.
От срубания потоков виндой все-равно не спасет! :)


 
Пробегал2...   (2008-04-25 18:12) [58]

Loginov Dmitry ©   (25.04.08 18:06) [57]
Практически тот же изврат, что и при while Assigned(MyThread) do...
но более правильный


ну нифига... У тебя все расчитано, что максимум будет создан один поток.. Второй поток уже внесет несусветную суматоху. У меня же поток по всем правилам ООП написан ;) Поменяются тольуо функции ожидания для нескольких потоков, будет использована WaitForMultipleObjects

Loginov Dmitry ©   (25.04.08 18:06) [57]
От срубания потоков виндой все-равно не спасет! :)


да с чего ты взял, что винда потоки срубает?!?! Винда финализирует потоки не при FreeLibrary, а при завершении процесса, тогда она все ресурсы пытается освободить, созданные этим процессом.


 
Игорь Шевченко ©   (2008-04-25 19:05) [59]


> странно... даже как-то бредово... А где же тогда рекомендуется
> завершать потоки, созданные в DLL? Ведь идеологически при
> DLL_PROCESS_DETACH самое место...


потоки не создаются в DLL. Потоки создаются в процессе. Познай эту истину и будет тебе рулез вечный и немеряный


 
Пробегал2...   (2008-04-25 19:18) [60]

Игорь Шевченко ©   (25.04.08 19:05) [59]
потоки не создаются в DLL. Потоки создаются в процессе


а вам доставляет удовольствие цепляться к словам? Любопытно посто.

Если действительно непонятна фраза "потоки созданные в DLL" - я уточню. Это потоки, которые созданы в том месте маш. кода, который был загружен в ВАП процесса при проецировании образа DLL.

К чему еще изволите придраться?


 
Игорь Шевченко ©   (2008-04-25 19:23) [61]

Пробегал2...   (25.04.08 19:18) [60]

И чем они отличаются от потоков, созданных в другом месте кода ?


 
Сергей М,   (2008-04-25 19:30) [62]


> Пробегал2...   (25.04.08 19:18) [60]


> Это потоки, которые созданы в том месте маш. кода, который
> был загружен в ВАП процесса при проецировании образа DLL


Не нало месить кислое и фиолетовое.


 
Пробегал2...   (2008-04-25 20:35) [63]

Игорь Шевченко ©   (25.04.08 19:23) [61]
И чем они отличаются от потоков, созданных в другом месте кода ?


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

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

С таким же успехом и глобальные переменные, выделенные в DLL - можно очищать в самом приложении, не так ли? Только это нелогично, это - неудобно.

Плюс весь WinApi спроектирован таким образом и я думаю стоит придерживаться этого подхода. Когда вам требуется принять некую структуру данных - ВЫ выделяете память под нее и передаете на нее ссылку, по которой эта структура заполняется библиотекой.
Заметьте, не библиотека выделяет память и передает ссылку на заполненную структуру, которую ВЫ потом должны уничтожить.
Принцип один: если намусорил - подчисти за СОБОЙ.

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


 
Игорь Шевченко ©   (2008-04-25 20:42) [64]

Пробегал2...   (25.04.08 20:35) [63]

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


 
Сергей М,   (2008-04-25 20:43) [65]

Удалено модератором


 
Сергей М,   (2008-04-25 21:00) [66]

Здесь

http://msdn2.microsoft.com/en-us/library/ms885202.aspx

черным по белому нарисована логика вызовов DllMain с тем или иным резоном.

С учетом же того, что вызовы DllMain конкретного модуля в контексте текущего процесса осуществляются системой синхронно, "феномен зависания" становится совершенно очевидным и понятным, даже без вникания во всякие там PEBы и TIBы.


 
Сергей М,   (2008-04-25 21:01) [67]

))


 
Loginov Dmitry ©   (2008-04-25 21:12) [68]

> ну нифига... У тебя все расчитано, что максимум будет создан
> один поток.. Второй поток уже внесет несусветную суматоху.


мне просто не требуется, чтобы в программе крутилось несколько однотипных потоков, поэтому выбрано такое решение. В других случаях будет выбрано другое решение. Кстати, после WaitForSingleObject() стоило бы выждать хотябы немного времени (например 50 / 100 мс), чтобы стать более уверенным, что функция потока уперлась в ExitThread() (а этот код уже выполняется не в нашей DLL-ке, а в kernel) и ожидает снятия блокировки, в этом случае выгрузка библиотеки уже точно ни на что не повлияет, и никаких AV не будет.

> да с чего ты взял, что винда потоки срубает?!?! Винда финализирует
> потоки не при FreeLibrary


Вот срубает и все тут. Причем FreeLibrary() перед закрытием программы вызывается. В одном случае срубает, в другом - не срубает. Есть конечно подозрение, что где-то в системе лишний раз LoadLibrary() для моей библиотеки кто-то вызвал, но наврядли... Проверю на следующей неделе.


> И чем они отличаются от потоков, созданных в другом месте
> кода ?

Потоковой функцией, код которой выгружается вместе с DLL-кой в том случае, если он принадлежит DLL-ке.


 
Пробегал2...   (2008-04-25 21:42) [69]

Игорь Шевченко ©   (25.04.08 20:42) [64]
Это всего лишь твое имхо. Ни больше, ни меньше


ок, давайте по другому. Игорь, только честно - у вас были проекты, где поток создавался в коде DLL, а завершался в другом месте?

P.S. Это я уже не говорю об использовании класса TThread, там уж как ни крути финализировать надо там же, где создаешь.


 
Игорь Шевченко ©   (2008-04-25 22:50) [70]

Пробегал2...   (25.04.08 21:42) [69]

Я не единственный разработчик программного обеспечения на этом свете.
И на слабо бери кого-нибудь другого.


 
Пробегал2...   (2008-04-25 23:40) [71]

Игорь Шевченко ©   (25.04.08 22:50) [70]

как я понимаю, ответ нет, ни в одном проекте вы так не делали.
Что и требовалось доказать.


 
Игорь Шевченко ©   (2008-04-26 00:13) [72]

Пробегал2...   (25.04.08 23:40) [71]

И на слабо бери кого-нибудь другого. Доказатель фигов


 
Пробегал2...   (2008-04-26 02:52) [73]

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

Да, это мое ИМХО. ну не согласны - и не согласны ;)


 
Игорь Шевченко ©   (2008-04-26 11:02) [74]

Пробегал2...   (26.04.08 02:52) [73]

С точки зрения процесса, и основное приложения и DLL - это всего лишь части кода, связанные в единое адресное пространство (про различные менеджеры памяти или VMT для дельфийских классов в этом аксепте упоминать сейчас не будем, так как консепция потоков не зависит от используемого языка программирования). Таким образом, ничто не мешает потоку, начавшись в одной части кода. завершится абсолютно в другой.
Система это делать позволяет.
Более того, система даже позволяет создать поток в одном приложении, а завершать его в другом, используя функции CreateRemoteThread и TerminateThread

Давайте все-таки различать - возможно/невозможно и удобно/неудобно


 
Котик Б   (2008-04-26 16:48) [75]

Игорь, я Вами восхищаюсь :)


 
имя   (2008-04-26 20:28) [76]

Удалено модератором


 
Экс-Оригинал   (2008-04-26 21:59) [77]

Кто-нибудь может привести пример(свой, чужой - неважно),
где бы в DLL создавался поток (BeginThread/TThread), затем DLL из приложения выгружалась бы, а поток продолжал работать?


 
Loginov Dmitry ©   (2008-04-26 22:08) [78]

> где бы в DLL создавался поток (BeginThread/TThread), затем
> DLL из приложения выгружалась бы, а поток продолжал работать?


На ум приходит только одно: потоковая функция расположена в другом модуле.


 
guav ©   (2008-04-26 22:22) [79]


> > где бы в DLL создавался поток (BeginThread/TThread), затем
> > DLL из приложения выгружалась бы, а поток продолжал работать?
> На ум приходит только одно: потоковая функция расположена
> в другом модуле.


Потоковая это только адрес начала потока, далее может быть переходы куда угодно. Легко представить себе начало потока в dll, затем продолжение в другой и вызов ExitThread уже оттуда. Можно даже и без ExitThread, обычным возвратом, просто переход в другой модуль не по call, а по jmp.
Или так: в потоке создаются фиберы, и поток завершается в фибере созданном в другом модуле.


 
Экс-Оригинал   (2008-04-26 22:53) [80]


> На ум приходит только одно: потоковая функция расположена
> в другом модуле.


т.е.?



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

Текущий архив: 2008.06.08;
Скачать: CL | DM;

Наверх




Память: 0.65 MB
Время: 0.027 c
2-1210878036
Zoom
2008-05-15 23:00
2008.06.08
Transparent Bitmap и Cаnvas?


4-1190539420
Jeeb
2007-09-23 13:23
2008.06.08
Список модемов Windows XP


3-1199315982
Николай_1
2008-01-03 02:19
2008.06.08
Нужна помощь


15-1208952397
Ega23
2008-04-23 16:06
2008.06.08
Можно ли приблизительно оценить длину записи


15-1209054886
БарЛог
2008-04-24 20:34
2008.06.08
VBScript дописать файл