Форум: "Начинающим";
Текущий архив: 2006.03.05;
Скачать: [xml.tar.bz2];
ВнизПроблема с 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;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.012 c