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

Вниз

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

 
Loginov Dmitry ©   (2008-04-23 23:32) [0]

Не так давно я достаточно негативно отзывался о реализации класса TThread в Delphi с завлением, что потоки при завершении иногда виснут. Все-таки тогда правда была не на моей стороне. Просто потоки крутятся в различных DLL-ках, и вполне закономерно, что при выгрузке с помощью FreeLibrary библиотеки, в которой еще крутятся потоки, происходят различные неприятные вещи.

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

Потоки невозможно уничтожить в секции finalization библиотеки. Пробовать бесполезно, могут быть лишь такие варианты:
- винда срубает потоки перед выгрузкой библиотеки. В этом случае возникнет зависание при вызове TThread.Free(), т.к. будет вечно ожидаться завершение работы несуществующего потока. Данный вариант происходит при завершении работы процесса.
- потоки не срубаются. Выгрузка библиотеки с работающими потоками приводит к Access Violation, процесс может завершиться.
- если и EXE и DLL скомпилированы с пакетами, то выгрузка библиотеки не приводит к AV, однако и потоки не завершаются.

Дополнительно: потоки нельзя завершить в finalization DLL-ки, поскольку, выгрузка библиотеки и завершение потока - взаимоисключающие вещи. Поток можно создать в initialization библиотеки, однако Windows его не запустит, пока инициализация библиотеки не завершится (эти вещи также взаимоисключающие).

Все сказанное ни коим образом не относится к работе EXE - там таких проблем не возникает.

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


 
ага   (2008-04-24 01:00) [1]


>
> Loginov Dmitry ©   (23.04.08 23:32)  

Какой ужжасс...


 
Tirael   (2008-04-24 02:15) [2]

а зачем отдельная ф-ция? перед выгрузкой же автоматически вызывается дллмайн с параметром длл_процесс_детач...


 
KSergey ©   (2008-04-24 07:35) [3]

Предлагаю в след. раз почитать Рихтера про потоки, процессы и т.д.
Т.к., с одной стороны, описаны стандартные грабли новичков, с другой - и, думаю, автор уже это понимает - все они исходят из вообще непонимания принципов работы потоков в виндовс, к которым TThread является лишь оберткой. Причем обертка эта такова, что она фактически очень слабо управляет собтвенно виндовыми объектами потоков, которыми, впрочем, "мощнее" управлять и нельзя по определению.
Однако легкость и податливость в управлении "с наружи" другими, "более простыми" компонентами, действительно сильно сбивает с толку при попытках так же "по простому" манипулировать экземпляром объекта TThread.


 
Loginov Dmitry ©   (2008-04-24 07:56) [4]

> описаны стандартные грабли новичков


Думаю, что и не только новичков. В той же степени это касается и профессионалов, еще не успевших по каким-либо причинам прочитать MSDN на все 100%


 
KSergey ©   (2008-04-24 08:54) [5]

> Loginov Dmitry ©   (24.04.08 07:56) [4]
> не успевших по каким-либо причинам
> прочитать MSDN на все 100%

В данном вопросе и объеме достаточно почитать Рихтера, эта тема у него очень подробно раскрыта. Могу ошибаться, проверять не буду, но пожалуй у него легко найти ответы на все вопросы из серии "че за нафик?" по всем упомянутым граблям.


 
Котик Б   (2008-04-24 09:26) [6]

Пользуйтесь напрямую CreateThread - в чём проблема то :)
Будете потом всё валить на WinAPI вместо TThread...


 
Сергей М. ©   (2008-04-24 10:01) [7]


> Loginov Dmitry ©   (23.04.08 23:32)


> Потоки невозможно уничтожить в секции finalization библиотеки


Возможно.
Невозможно другое - дождаться сигнала завершения потока в функции ожидания
Объясняется это довольно просто - в момент финализации dll-модуля PEB текущего процесса заблокирован и система не может внести туда изменения, касаемые TIB завершившегося потока, а флаг завершения потока система может поднять только после внесения этих изменений.
Т.е. в этой ситуации получается нечто вроде дедлока.


 
DiamondShark ©   (2008-04-24 15:14) [8]


> Котик Б   (24.04.08 09:26) [6]
> Пользуйтесь напрямую CreateThread - в чём проблема то :)

Попользуйся -- узнаешь.


 
Пробегал2...   (2008-04-24 15:28) [9]

ну собственно что и требовалось доказать. Помню ту тему, где с апломбом утверждалось, что потоки в Delphi реализованы криво ;) Смешно. Просто кто-то не умеет их готовить.

Все остальное было сказано верно - читайте Рихтера, особенно про константы, передаваемые в главную функцию DLL. Никакой такой специальной функции экспортируемой библиотекой не надо, MS уже давно все продумала ;)


 
KSergey ©   (2008-04-24 15:44) [10]

> DiamondShark ©   (24.04.08 15:14) [8]
> Попользуйся -- узнаешь.

Суть не в том, что "узнаешь", а в том, кто виноват. :)


 
Loginov Dmitry ©   (2008-04-25 01:03) [11]

> Все остальное было сказано верно - читайте Рихтера, особенно
> про константы, передаваемые в главную функцию DLL. Никакой
> такой специальной функции экспортируемой библиотекой не
> надо, MS уже давно все продумала ;)


Перечитал Рихтера (по части многопоточности). Но все им написанное я уже читал в Windows SDK.
Т.е. есть допустим некая функция DllMain, в которую передается один их 4-х параметров. Насколько я понимаю, параметры DLL_THREAD_ATTACH и DLL_THREAD_DETACH в данной проблеме врядли чем-либо помогут. А DLL_PROCESS_ATTACH и DLL_PROCESS_DETACH это тоже, что и initialization и finalization в DLL (если я правильно понял). Ну и что же тогда "MS уже давно продумала"?


 
Экс-Оригинал   (2008-04-25 01:55) [12]

Что-то  непонятно, вообще о чем разговор.
Потоки вообще, не привязаны к какой-то конкретной билиотеке. К процессу приявязаны, если так можно выразиться.


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

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 его не запустит, пока инициализация библиотеки не завершится (эти вещи также взаимоисключающие).


я не понимаю, откуда ты черпаешь информацию.


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

возможно ты не понимаешь как завершать потоки в DLL?

Вызывается FreeLibrary из приложения. Соответственно, в контексте того же потока вызывается DLL_PROCESS_DETACH (поговорим пока о нем, а не о DLL_THREAD_...), и судя по всему вызывается finalization модулей библиотеки в Delphi.

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

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

Это - правильная идеология работы.


 
Loginov Dmitry ©   (2008-04-25 07:58) [15]

> я не понимаю, откуда ты черпаешь информацию.


1. Столкнулся с проблемой на практике, сделал выводы
2. Подкрепил правильность выводов прочтением Windows SDK и Рихтера

> Код обработки должен быть следующий - запущенным потокам
> делается Terminate, после чего идет WaitFor... функции,
> ожидающие завершения этих потоков.
>
> В самих потоках естественно должна быть своевременная проверка
> на Terminated свойство потока, и если оно выставлено - кратчайшим
> способом финализировать все ресурсы и удалять поток.
>
> Это - правильная идеология работы.


Но эта идеология не работает! Об этом в [0] как раз и идет разговор ;)


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


> это ты с чего взял? Потоки, созданные в DLL - надо завершать
> в DLL, а потоки, созданные в другом месте - ндадо завершать
> в соответствующем месте.


Какая разница, где завершать потоки (потоки Windows, а не Delphiйские TThread) ?


 
Loginov Dmitry ©   (2008-04-25 10:42) [17]

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


 
Сергей М. ©   (2008-04-25 10:48) [18]


> Loginov Dmitry ©   (25.04.08 10:42) [17]


Проиллюстрируй в коде ..


 
Loginov Dmitry ©   (2008-04-25 11:14) [19]

> Проиллюстрируй в коде ..


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); // Ошибка есть
 //CreateThread(nil, 0, @MyThreadFunc, 0, 0, I); // Ошибок нет
end;

initialization
 StartMythread;
finalization
 Term := True;

 // Ожидаем завершения потока
 while Term do
   Sleep(0); // AV валятся здесь!!!

 Sleep(0); // Чисто в отладочных целях
end.


DLL и ЕХЕ скомпилированы без пакетов.

В ЕХЕ две кнопки: с LoadLibrary и FreeLibrary


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


> // Ошибок нет


"Ты суслика видишь ? И я не вижу. А он есть !"  (с)


> // Ожидаем завершения потока


Это вовсе не ожидание завершения потока, а всего лишь ожидание момента когда переменная Term примет значение False.


> Sleep(0); // AV валятся здесь


Именно здесь AV валиться не может, не выдумывай.
Неоткуда тут AV взяться.


 
guav ©   (2008-04-25 11:28) [21]

> BeginThread(nil, 0, @MyThreadFunc, 0, 0, I); // Ошибка есть

Ошибка в твоём коде.


 
Loginov Dmitry ©   (2008-04-25 11:29) [22]


> Именно здесь AV валиться не может, не выдумывай.
> Неоткуда тут AV взяться.


Я ведь когда-то тоже так думал! А оказывается, что есть откуда.
И именно на Sleep(0)...


 
Loginov Dmitry ©   (2008-04-25 11:29) [23]


> Ошибка в твоём коде.


Где именно?


 
Сергей М. ©   (2008-04-25 11:30) [24]


> BeginThread(nil, 0, @MyThreadFunc, 0, 0, I); // Ошибка есть


При объявлении MyThreadFunc ты указал соглашение stdcall  - вот граблями и получил)


 
guav ©   (2008-04-25 11:31) [25]

Смотри что передаёшь в BeginThread и что она требует.


 
guav ©   (2008-04-25 11:31) [26]

Кстати, если писать такое без @, то компилятор будет проверять прототип.


 
Сергей М. ©   (2008-04-25 11:32) [27]


> именно на Sleep(0)


То, что остановившись на этой строчке по брейкпойнту ты жмакнул F7 и получил AV, вовсе не означает, что именно эта строчка является причиной исключения.


 
Loginov Dmitry ©   (2008-04-25 11:42) [28]

Мда... Неудачный получился пример. Сорри за невнимательность :(
Но один фик с TThread AV гарантированно лезут...


 
Сергей М. ©   (2008-04-25 11:44) [29]


> один фик с TThread AV гарантированно лезут


Иллюстрируй в коде теперь с TThread..


 
Loginov Dmitry ©   (2008-04-25 11:55) [30]


> Иллюстрируй в коде теперь с 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;

{ TMyThread }

procedure TMyThread.Execute;
begin
 inherited;
 //FreeOnTerminate := True; // Не влияет
 while not Terminated do
   Sleep(50);
end;

initialization
 StartMythread;
finalization
 // сценарий 1
 //MyThread.Terminate; // возникает AV

 // сценарий 2
 //MyThread.Free; // Программа зависает

 // сценарий 3
 //MyThread.Terminate; // Программа
 //MyThread.WaitFor;   // зависает

 // сценарий 4
 // Ничего не делаем - возникает AV
end.


Опять ведь найдете че-нибудь.... :)


 
Сергей М. ©   (2008-04-25 12:00) [31]


> Loginov Dmitry ©   (25.04.08 11:55) [30]


1,4 - ничего удивительного, абсолютно ожидаемый результат.
2,3 - см. [7] на предмет причины зависания


 
Loginov Dmitry ©   (2008-04-25 12:13) [32]


> 1,4 - ничего удивительного, абсолютно ожидаемый результат.
>
> 2,3 - см. [7] на предмет причины зависания


Да насчет зависаний как раз-то все и понятно. Windows SDK об этом говорит вполне четко.

Таким образом получается, что в любом случае мы ловим или AV, или зависание.
А можно ли это как-то обойти без дополнительной экспортируемой функции? И как?


 
Сергей М. ©   (2008-04-25 12:39) [33]


> Loginov Dmitry ©   (25.04.08 12:13) [32]
>
>


Обойти в принципе можно, но реализация обхода будет уже из разряда трюков.
А трюки, как известно, штука рискованая и ненадежная)


 
Loginov Dmitry ©   (2008-04-25 12:52) [34]

кстати, в finalization такой трюк обычно спасает:


 MyThread.Terminate;
 while Assigned(MyThread) do
   Sleep(20);


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


 
Сергей М. ©   (2008-04-25 12:58) [35]


> такой трюк обычно спасает


С какого перепугу ?


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

Игорь Шевченко ©   (25.04.08 9:42) [16]
Какая разница, где завершать потоки (потоки Windows, а не Delphiйские TThread) ?


а зачем в дельфи лишать себя такого удобного класса, как TThread?


 
Loginov Dmitry ©   (2008-04-25 13:40) [37]


> С какого перепугу ?


В конце TMyThread.Execute (или в деструкторе) выполняется присвоение MyThread := nil


 
Игорь Шевченко ©   (2008-04-25 14:01) [38]

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

Тут в ветке где-то про чтение Рихтера говорили. Нафига читать Рихтера, если он в Delphi ни ухом ни рылом ?


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


> Loginov Dmitry ©   (25.04.08 13:40) [37]


> В конце TMyThread.Execute (или в деструкторе) выполняется
> присвоение MyThread := nil
>


И что ?

Появление какой-то там nil в какой-то там переменной не есть факт завершения поточной функции.

Это те же фаберже, чт о и в [19], только вид сбоку)

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


 
Пробегал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]


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


т.е.?


 
Пробегал2...   (2008-04-27 12:32) [81]

Игорь Шевченко ©   (26.04.08 11:02) [74]
Давайте все-таки различать - возможно/невозможно и удобно/неудобно


а процитируйте хоть одно мое слово, где я говорю, что это невозможно?

На протяжении всей ветки я говорил, что это НЕПРАВИЛЬНО, а не невозможно. С точки зрения логики путанно. Поэтому в моих проектах и я уверен в ваших такого нету.

И не понимаю почему вы начинаете спорить, а потом зачем-то говорите про различие слов невозможно и неудобно, очевидно с самого начала говорили про неудобно.


 
Игорь Шевченко ©   (2008-04-27 12:35) [82]

Пробегал2...   (27.04.08 12:32) [81]


> На протяжении всей ветки я говорил, что это НЕПРАВИЛЬНО,
>  а не невозможно


Что есть НЕПРАВИЛЬНО ? Ты, прежде чем претендовать на абсолютную истину, определись с терминологией.
А то как в детском саду - "а я имел в виду другое!"


 
Пробегал2...   (2008-04-27 13:00) [83]

Игорь Шевченко ©   (27.04.08 12:35) [82]
Что есть НЕПРАВИЛЬНО ?


ну в принципе конечно можно понять неправильно с той точки зрения, что работать не будет правильно.

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


 
Игорь Шевченко ©   (2008-04-27 13:29) [84]

Пробегал2...   (27.04.08 13:00) [83]

Насчет "работать не будет правильно" - если система позволяет, почему не будет работать ?


> Я имел в виду неправильно с той точки зрения, что так делать
> не принято, то есть плохой подход


А если не секрет, где есть свод правил, где однозначно написано, как принято делать, как не принято, что есть плохой подход, что есть хороший подход ?
То есть, я понимаю, что Дейкстра сказал "goto considered harmful" и это занесено на скрижали. Так вот, есть такие же скрижали относительно потоков в DLL ?

Я до сих пор считал, если система позволяет нечто сделать, то вне зависимости от личных приверженностей, это нечто имеет право на существование.


 
Сергей М,   (2008-04-27 13:39) [85]


> Экс-Оригинал   (26.04.08 22:53) [80]


> т.е.?


Что не понятно ?


 
Экс-Оригинал   (2008-04-27 13:45) [86]


> Что не понятно ?


Непонятно, что имеется ввиду. Что за другой  модуль?


 
Сергей М,   (2008-04-27 13:53) [87]


> Что за другой  модуль?


Любой PE-модуль в ВАП текущего процесса, содержащий исполняемый код поточной функции, адрес которой был указан при вызове CreateThread.


 
Экс-Оригинал   (2008-04-27 15:26) [88]


> юбой PE-модуль в ВАП текущего процесса, содержащий исполняемый
> код поточной функции, адрес которой был указан при вызове
> CreateThread.


Да, действительно получается. Но только в случае CreateThread.
При использовании BeginThread - AV.


 
Сергей М,   (2008-04-27 15:34) [89]


> Экс-Оригинал   (27.04.08 15:26) [88]



> При использовании BeginThread - AV


И это вполне объяснимо.
Различия этих ф-ций надо знать как отче наш.


 
MetalFan ©   (2008-04-27 15:43) [90]

можно и мои 5 копеек!
1. цитата из MSDN по поводу DLLMain
Because DLL notifications are serialized, entry-point functions should not attempt to communicate with other threads or processes. Deadlocks may occur as a result.
откуда можно сделать вывод, что управлять другими потоками (создавать при DLL_PROCESS_ATTACH и убивать при DLL_PROCESS_DEATTACH) не стоит.
откуда можно сделать вывод, что для очистки ресурсов, занятых из DLL, стоит предусмотреть отдельную функцию и вызывать ее "снаружи" перед выгрузкой библиотеки....

> procedure DLLEntryPoint(Reason: DWORD);
> begin
>  case Reason:
>    Dll_Process_Attach: ДЕЛАЕМ ИНИЦИАЛИЗАЦИЮ БИБЛИОТЕКИ
>    DLL_PROCESS_DETACH: ДЕЛАЕМ ФИНАЛИЗАЦИЮ ВСЕХ ДАННЫХ БИБЛИОТЕКИ
>  end;
> end;

делать инициализацию и финализацию надо с большой осторожностью, ибо опять же судя по библии мсдн:
Calling functions that require DLLs other than Kernel32.dll may result in problems that are difficult to diagnose. For example, calling User, Shell, and COM functions can cause access violation errors, because some functions load other system components. Conversely, calling functions such as these during termination can cause access violation errors because the corresponding component may already have been unloaded or uninitialized.


 
Сергей М,   (2008-04-27 16:47) [91]


> Экс-Оригинал   (27.04.08 15:26) [88]


Ф-ция BeginThread принимает параметром адрес функции ThreadFunc , не являющейся поточной.
Поточная функция применительно к BeginThread - это раппер в коде юнита system, а не та самая ThreadFunc.


 
Пробегал2...   (2008-04-28 00:31) [92]

Игорь Шевченко ©   (27.04.08 13:29) [84]

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

Поэтому я и предложил самое простое - вопрос "а вы так когда-нибудь делали?". Я уверен, что с потоками вы работали ОЧЕНЬ МНОГО, вы хоть раз применяли такую тактику, как создание в одной DLL, а завершение потока в другой DLL? Я думаю нет у вас такого кода.

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


 
Пробегал2...   (2008-04-28 00:34) [93]

Игорь Шевченко ©   (27.04.08 13:29) [84]
Я до сих пор считал, если система позволяет нечто сделать, то вне зависимости от личных приверженностей, это нечто имеет право на существование


право на существование понятие относительное. Элементарный пример:

procedure TForm1.SomeMethod(...);
begin
 if Form1.blabla ...
end;


можно ли так писать? Можно.
Встретишь ли такой код у профессионала? Не встретишь.

И доказывать это я смысла не вижу. Вы упираетесь мол так делать можно. Да, можно, с этим никто не спорит, но этой если угодно плохой подход.


 
Игорь Шевченко ©   (2008-04-28 00:39) [94]

Пробегал2...   (28.04.08 00:34) [93]


> право на существование понятие относительное. Элементарный
> пример:
>
> procedure TForm1.SomeMethod(...);
> begin
>  if Form1.blabla ...
> end;
>
> можно ли так писать? Можно.
> Встретишь ли такой код у профессионала? Не встретишь.


Не надо сравнивать кислое и мягкое. Это плохой прием в дискуссии.

Пробегал2...   (28.04.08 00:31) [92]


> Поэтому буду считать, что так делать некошерно


Мы все-таки на форуме delphimaster.ru, а не в синагоге и разговор у нас идет не о толковании торы, верно ?

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


 
Пробегал2...   (2008-04-28 01:25) [95]

Игорь Шевченко ©   (28.04.08 0:39) [94]
Не надо сравнивать кислое и мягкое. Это плохой прием в дискуссии


тогда поясните, чем код:

procedure TForm1.SomeMethod(...);
begin
if Form1.blabla ...
end;


не удовлетворяет фабуле: "Я до сих пор считал, если система позволяет нечто сделать, то вне зависимости от личных приверженностей, это нечто имеет право на существование"


 
Игорь Шевченко ©   (2008-04-28 09:56) [96]

Пробегал2...   (28.04.08 01:25) [95]

А что, приведенный тобой код не работает ?

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


 
Пробегал2...   (2008-04-28 12:44) [97]

Игорь Шевченко ©   (28.04.08 9:56) [96]
А что, приведенный тобой код не работает ?


э-э-э... так в том и дело, что работает. Также как завершение потока работает в другом модуле.

Игорь, вы суть дискуссии помните? Мы сначала говорили о заершении потока в другом модуле, потом я вам привел пример из [95], вы сказали что это совершенно другой случай.

Я и прошу объяснить, почему это совершенно другой случай? Он ведь работает? Как раз работает. Система (в данном случае delphi) позволяет писать такой код? Позволяет.

По вашему же утверждению:

"если система позволяет нечто сделать, то вне зависимости от личных приверженностей, это нечто имеет право на существование"

Так в чем разность примеров?


 
Игорь Шевченко ©   (2008-04-28 12:48) [98]

Пробегал2...   (28.04.08 12:44) [97]

Ты путаешь кислое с мягким. Код в [95] работает. К завершению потоков в другом модуле не имеет ни малейшего отношения. Твои религиозные предпочтения я не вижу никакого смысла обсуждать.


 
Пробегал2...   (2008-04-28 13:29) [99]

Игорь Шевченко ©   (28.04.08 12:48) [98]
Код в [95] работает


Код, который будет вызывать CreateThread в одной DLL, а ExitThread делать в другой DLL тоже будет работать.


 
Игорь Шевченко ©   (2008-04-28 13:40) [100]

Пробегал2...   (28.04.08 13:29) [99]

Наконец мы пришли к согласию. На этой радостной ноте можно завершить дискуссию ?


 
Пробегал2...   (2008-04-28 16:23) [101]

Игорь Шевченко ©   (28.04.08 13:40) [100]
Наконец мы пришли к согласию


почему пришли? Я как понял вы читали мои посты крайне невнимательно, по диагонали. Если вдруг перепрочтете (ну вдруг оказия такая случится) - можете подтвердить, что ни я ни вы обратного абсолютно нигде не утверждали. То есть, это не "пришли", эти постулаты были изначально озвучены и разговор был совершенно о другом.

Удивляет невнимательность и какая-то прямота процесса.

Я специально выдумал некорректный пример программирования, но чтобы он был рабочим (таким же рабочим как и завершение потока в другом модуле). На что вы мне отвечаете:

"А что, приведенный тобой код не работает ?"

Получилось что-то вроде такого:

- говорить "рок против наркотиков" тоже самое, что "гринпис против деревьев"
- но ведь гринпис любит деревья!


После этого хоть стой хоть падай. То ли человек не понял смысла первой фразы, то ли прочитал за 0,1 секунду и даже осмыслить не успев, выдал ответ.


 
Сергей М. ©   (2008-04-28 16:33) [102]


> Пробегал2...   (28.04.08 13:29) [99]
>
> Код, который будет вызывать CreateThread в одной DLL, а
> ExitThread делать в другой DLL тоже будет работать


Может и будет, но не обязан.


 
Игорь Шевченко ©   (2008-04-28 16:34) [103]

Пробегал2...   (28.04.08 16:23) [101]

Ввиду моей неоспоримой правоты дальнейшую дискуссию считаю нецелесообразной.


 
Пробегал2...   (2008-04-28 16:35) [104]

впрочем, могу выдать и другую версию, более эмоциональную, иногда так понятнее:

Игорь Шевченко ©   (28.04.08 9:56) [96]
А что, приведенный тобой код не работает ?


он как раз, блин, работает. Я специально его придумывал, чтобы он был рабочим. Чтобы он был "неправильным", но рабочим. Чтобы этот код полностью удовлетворял вашей фразе:

"если система позволяет нечто сделать, то вне зависимости от личных приверженностей, это нечто имеет право на существование"

Так вот делфьи позволяет писать такой код, но это не значит что такой код стоит писать. Это называется А-Н-А-Л-О-Г-И-Я, я его привел как пример аналогичный примеру завершения потока в другом модуле. Да, windows позволяет так делать, но это не означает что так делать правильно! И с этой точки зрения я вас три раза попросил объяснить в чем РАЗНИЦА между этими примерами.

Вы три раза повторили "не путай кислое с мягким". Я понимаю, что эта фраза вам очень по душе, но я понял с первого раза и в дальнейшем просил ОБЪЯСНИТЬ различие этих примеров. Но вы ни слова ни сказали про то, чем они отличаются. Вы только повторяли "это - кислое", а "это - мягкое".

Но я не вижу разницы. Эти оба примера рабочих, в первом случае так позволяет делать windows, во втором случае так позволяет делать delphi. И то, что система позволяет так делать НЕ означает что так делать правильно.

И еще раз. Пример с кодом:

procedure TForm1.SomeMethod(...);
begin
 if Form1.blabla ...
end;


специально был предоставлен как ОПРОВЕРЖЕНИЕ фразы "если система позволяет нечто сделать, то вне зависимости от личных приверженностей, это нечто имеет право на существование". Потому что этот пример:

1) полностью рабочий и более того в простенькой программе будет идентичен правильному аналогу "Self.blabla...".

А значит этот пример полностью удовлетворяет условию изречения: "если система позволяет нечто сделать" - система ПОЗВОЛЯЕТ ТАК ДЕЛАТЬ, потому что пример - РАБОЧИЙ.

2) у профессиональных программистов рука не поднимется сказать, что так писать правильно или даже такой код имеет право на существование. Если человек понимает что он делает - он так писать НЕ БУДЕТ.

Поэтому данный пример опровергает ваше изречение. Потенциальная возможность что-то делать не означает, что так делать ПРАВИЛЬНО.

Ну так понятнее?


 
Пробегал2...   (2008-04-28 16:39) [105]

Сергей М. ©   (28.04.08 16:33) [102]
Может и будет, но не обязан


АБСОЛЮТНО ТОЧНО ТАКЖЕ пример с:

procedure TForm1.SomeMethod(...);
begin
if Form1.blabla ...
end;


может и будет рабочим. Но не обязан. Не согласен?

Вот ты сейчас какой-то критерий вывел "код может быть рабочим, а может и нет". Но это точно также относится к моему примеру.

Поэтому я и называю этот пример аналогичным, а ИШ не согласен, считая что это совершенно типа разные примеры. На вопрос "чем они разные" и почему их нельзя считать аналогичными ответа озвучено не было, хотя вопрос такой задан неоднократно. Только как молитва "ты путаешь кислое с мягким", но никакого пояснения.

Игорь Шевченко ©   (28.04.08 16:34) [103]

Ввиду моей неоспоримой правоты дальнейшую дискуссию считаю нецелесообразной


ну как вам считать - это ваше личное право. Как угодно ;)



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

Форум: "Прочее";
Текущий архив: 2008.06.08;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.79 MB
Время: 0.043 c
15-1209311696
boriskb
2008-04-27 19:54
2008.06.08
К Ega23


2-1211133293
Favorit
2008-05-18 21:54
2008.06.08
Работа с InterBase


15-1208945641
Armond
2008-04-23 14:14
2008.06.08
Компоненты


2-1210576078
TUser
2008-05-12 11:07
2008.06.08
Почему это до сих пор работало?


15-1209138083
@!!ex
2008-04-25 19:41
2008.06.08
Как настроить интернет через Linux комп?





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