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

Вниз

Проблема с GetMem   Найти похожие ветки 

 
YuRock ©   (2006-02-15 01:28) [0]

Добрый. Сомневался, в какую конференцию написать - решил сюда.
Итак, написал я прогу - загрузчик файлов (по HTTP, используя Indy - но это не важно). Функциональность закачки/докачки решил запихнуть
в dll, чтоб потом в других прогах использовать (ну очень мне понравилось, как я написал :) - работает на ура).

Хочу сразу сказать, что вызывается из dll одна функция:
function HTTPLoadFile( nFileHandle: Integer; sURL: LPSTR; sFileName: LPSTR; var nFileSize: Int64; var nFileReaded: Int64; bResume: BOOL ): BOOL;
При чем память под sURL и sFileName выделяется и удаляется в основном приложении. Более того - через GlobalAlloc(GMEM_FIXED).

Итак, приложение стало иногда (раз в час, примерно) страшно валиться (то просто прога "выйдет из себя" молча, то AV в этой dll). Я для тестов поднял апач на локальной машине и начал одновременно закачивать 100 файлов - стало валиться за 5-10 секунд.

Т.к. отладчик call-stack не показывал, я засунул в отдельный try..except каждую строчку кода этой функцции, в except"е поставил Exit и на нем - break-point.
И что интересно - исключение появлялось в рандомном порядке в разных местах, НО ЧАЩЕ ВСЕГО - при вызове GetMem - для выделения буффера (размер в зависимости от высчитанной текущей скорости закачки).

Я, ради прикола, заменил GetMem/FreeMem на GlobalAlloc/GlobalFree и падать стало в 20 раз реже - уже около минуты прога работала.
В том, что ошибка останется - я и не сомневался, т.к. я использовал Indy - а это классы, для выделения памяти под структуры объектов которых используется тот же GetMem.

Пробовал на D6, и D7, Win2k и XP - одно и то же.
После этого я попробовал подключить ShareMem - и все заработало идеально (даже с GetMem/FreeMem) (как раньше, до переноса ф-ции в отдельную dll).

Но т.к. ShareMem - это не выход (эту dll уже ждали несколько проектов на вижуале, да и вообще...), я исхитрился следующим образом:
убрал ShareMem, а вместо этого переопределил AllocMem/FreeMem/ReAllocMem на GlobalAlloc/GlobalFree/GlobalReAlloc через SetMemoryManager (только в dll).

И теперь все работает великолепно. Вот, появилось время написать сюда и задать вопрос - а что, в dll нельзя использовать стандартный GetMem? Почему? Ведь, вроде-бы, хоть image base и отдельный, но указатели в каждом модуле используются строго только в них...

Очень хотелось бы услышать мнение мастеров.
Спасибо.


 
YuRock ©   (2006-02-15 05:30) [1]

Пока писал этот вопрос - возникла одна идея, которая оказалась в итоге правильной - и я сам разобрался, в чем проблема :)

В принципе, это будет полезно для многих, поэтому расскажу.

Так вот, дело в том, что моя ф-ция из dll вызывалась из многих потоков, созданных BeginThread, но в основном приложении. Поэтому переменная IsMultiThread принимала значение True только в основной программе, а в dll оставалась равной False.

Просмотрев реализацию работы делфовой кучи (в частности - ф-цию SysGetMem), стало очевидно, что без синхронизации (которая начинает работать только если IsMultiThread = True) будут происходить страшные вещи :) Именно они и происходили.

Так что теперь при написании dll первое, что я обязательно буду делать - это писать IsMultiThread := True в инициализации. Мало ли, как будут вызываться ф-ции из нее... И всем рекомендую.


 
pargo ©   (2006-02-15 05:41) [2]

Интересно. Попробую. Я, тоже, ломал над этим голову.:((


 
Leonid Troyanovsky ©   (2006-02-15 09:10) [3]


> YuRock ©   (15.02.06 01:28)  

> в dll, чтоб потом в других прогах использовать (ну очень


Не очень понятно зачем, собс-но, dll.
Т.е., если другие нуждаются в закачке/докачке пусть пользуют саму
программу, которую можно сделать, например, консолью (для
работы в конвеере) или сервисом.

Это я к тому, что первое решение повлекло последующие,
не менее спорные.

--
Regards, LVT.


 
YuRock ©   (2006-02-15 09:25) [4]


> Leonid Troyanovsky ©   (15.02.06 09:10) [3]
>
> Не очень понятно зачем, собс-но, dll.
> Т.е., если другие нуждаются в закачке/докачке пусть пользуют
> саму
> программу, которую можно сделать, например, консолью (для
> работы в конвеере) или сервисом.


Немножко неясно, что именно "Не очень понятно" :)

Мне постоянно приходится работать с разными средствами: Delphi, VC, C#... вплоть до яваскрипта.
И почти всегда нужна ф-ция закачки файла. Проще всего (и быстрее, и удобнее, и возможностей больше) - запихнуть ее в dll и потом использовать, где угодно.


> Это я к тому, что первое решение повлекло последующие,
> не менее спорные.


Кгм...
1) Последующие спорные решения - это какие?
2) Сделать консоль или сервис - было бы менее спорным решением? :)


 
Leonid Troyanovsky ©   (2006-02-15 09:32) [5]


> YuRock ©   (15.02.06 05:30) [1]

> буду делать - это писать IsMultiThread := True в инициализации.
>  Мало ли, как будут вызываться ф-ции из нее... И


Предположим, что для собственных нужд dll нужен некий буфер,
(что уже, само по себе, вызывает много вопросов).
Ну и выделит она себе VirtualAlloc, сколько надо.
А использование операций, требующих неявного перераспределения
памяти (скажем, работа со строками) также необосновано в
условиях, что библиотека может применяться кем попало.

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

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2006-02-15 09:44) [6]


> YuRock ©   (15.02.06 09:25) [4]


> Мне постоянно приходится работать с разными средствами:
> Delphi, VC, C#... вплоть до яваскрипта.
> И почти всегда нужна ф-ция закачки файла. Проще всего (и
> быстрее, и удобнее, и возможностей больше) - запихнуть ее
> в dll и потом использовать, где угодно.

 Не dll.
 В том смысле, что это должен быть ActiveX, OLE server & other COM.

> 2) Сделать консоль или сервис - было бы менее спорным решением?
>  :)

Конечно. Чего может делать эта весчь?
Тащить файлы и сохранять их на диск.
Ну и, возможно, уведомлять клиента об окончании загрузки.

--
Regards, LVT.


 
YuRock ©   (2006-02-15 09:46) [7]


> Leonid Troyanovsky ©   (15.02.06 09:32) [5]
>
> Предположим, что для собственных нужд dll нужен некий буфер,
>
> (что уже, само по себе, вызывает много вопросов).
> Ну и выделит она себе VirtualAlloc, сколько надо.
> А использование операций, требующих неявного перераспределения
> памяти (скажем, работа со строками) также необосновано в
> условиях, что библиотека может применяться кем попало.


Это к чему? Не могу понять, извините...


> Т.е., одной только установки IsMultiThread - мало.


Хорошо, чего-то еще не хватает для полной уверенности? Я что-то упустил?


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


Есть другие варианты? Как написать гарантированно рабочую библиотеку, использующую стандартный делфовый менеджер памяти, не устанавливая IsMultiThread=True?


 
YuRock ©   (2006-02-15 09:57) [8]


> Leonid Troyanovsky ©   (15.02.06 09:44) [6]
>
>  Не dll.
>  В том смысле, что это должен быть ActiveX, OLE server &
> other COM.
>


Нет ничего быстрее и проще dll. А "ActiveX, OLE server & other COM" - добавят только тормозов и гемора с регистрацией и т.п.
Плюс использовать их гораздо более неудобно, чем dll. Больше писанины получится.

В общем, тут даже спорить не буду. Нет смысла.


>
> Конечно. Чего может делать эта весчь?
> Тащить файлы и сохранять их на диск.
> Ну и, возможно, уведомлять клиента об окончании загрузки.
>


Конечно. В данный момент - да. И еще все то, что я захочу и сделаю. И главное (повторяю еще раз) - простота и удобство использования.


 
Leonid Troyanovsky ©   (2006-02-15 10:10) [9]


> YuRock ©   (15.02.06 09:46) [7]

> Хорошо, чего-то еще не хватает для полной уверенности?


Для того, чтобы библиотека была гарантировано многопоточной
она не должна использовать то, что может сделать ее немногопоточной.
В частности, использование сторонних компонентов (как, собс-но,
_любых_ объектов). Т.е., всякое применение должно быть тщательно
изучено и обосновано. Что сделать в общем случае не просто, и гораздо
проще сделать работоспособное отдельно стоящее приложение.
Общение с таким приложением не представляет большого труда для
любых ЯВУ еще со времен ДОС.

Для иных случаев нужен COM, для чего и создавался.

--
Regards, LVT.


 
YuRock ©   (2006-02-15 10:16) [10]


> Для того, чтобы библиотека была гарантировано многопоточной
> она не должна использовать то, что может сделать ее немногопоточной.
>
> В частности, использование сторонних компонентов (как, собс-
> но,
> _любых_ объектов).


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


 
Leonid Troyanovsky ©   (2006-02-15 10:26) [11]


> YuRock ©   (15.02.06 10:16) [10]

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


Если приложение однопоточное, то оно им и будет
(всякие CreateRemoteThread не берем в рассмотрение).

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

--
Regards, LVT.


 
YuRock ©   (2006-02-15 10:34) [12]


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


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

При чем здесь теперь однопоточное приложение?


 
Leonid Troyanovsky ©   (2006-02-15 10:43) [13]


> YuRock ©   (15.02.06 10:34) [12]

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


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

--
Regards, LVT.


 
YuRock ©   (2006-02-15 10:46) [14]

Я тоже.


 
Defunct ©   (2006-02-16 01:46) [15]

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


Что-то не пойму я. А что есть какие-то ограничения на использование CS в dll?


 
Leonid Troyanovsky ©   (2006-02-16 08:25) [16]


> Defunct ©   (16.02.06 01:46) [15]

>  А что есть какие-то ограничения на использование CS в dll?


Есть и такие - нельзя использовать функции ожидания в dllproc.

Однако, мне тоже надоела эта тема.
Т.е., кто хочет делать dll из exe, пусть делают.
Надеюсь, что пользовать их мне не придется.

--
Regards, LVT.


 
evvcom ©   (2006-02-16 09:24) [17]


> function HTTPLoadFile( nFileHandle: Integer; sURL: LPSTR;
>  sFileName: LPSTR; var nFileSize: Int64; var nFileReaded:
>  Int64; bResume: BOOL ): BOOL;
> При чем память под sURL и sFileName выделяется и удаляется
> в основном приложении. Более того - через GlobalAlloc(GMEM_FIXED).

Не совсем ясно что, зачем и где.
Если dll используется из разных приложений, то об использовании дельфового менеджера для выделения памяти под данные, которые будут переданы в чужеродные приложения, надо забыть. Никаких ShareMem! Пусть внутри под строки, массивы память выделяет DelphiMM, но никакой передачи этих указателей наружу!
Далее. Память выделять можно 2-мя способами: 1) в exe и передавать указатель в dll, и 2) в dll, возвращая указатель в exe. Но освобождаться она должна там, где выделялась.
1 способ неудобен тем, что изначально в общем случае неизвестно требуемое количество байт, поэтому в данном случае лучше пойти по 2-му пути.
В твоей HTTPLoadFile не ясно использование nFileHandle. По моему разумению это должен быть var-параметр. sURL и sFileName должны быть const, опять же имхо. И тогда вроде становится многое логичным. Естественно должна быть тогда еще функция, которая по nFileHandle либо возвращает указатель на собственно сами данные, либо их копирует в память выделенную уже в host-приложении. И функция типа CloseHandle, которая и освобождает память (2-ой способ).



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

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

Наверх




Память: 0.54 MB
Время: 0.045 c
3-1136450830
sally
2006-01-05 11:47
2006.03.05
Увеличение скорости обработки данных


1-1138610482
rolex
2006-01-30 11:41
2006.03.05
Как избавиться в ListView от мерцания при прокрутке?


3-1136739867
IntruderLab
2006-01-08 20:04
2006.03.05
Работа с DAT файлами


6-1132634383
hed
2005-11-22 07:39
2006.03.05
Передача файла на apache сервер


3-1136462326
ViktorZ
2006-01-05 14:58
2006.03.05
Восстановление corrupted базы.