В последних версиях Delphi 11 Update 3 была внесена поправка в реализацию библиотеки RTL (Run-Time Library) для устранения проблемы, связанной с освобождением библиотек в пуле потоков (TThreadPool). Однако эта поправка не всегда работает корректно, особенно при использовании BPL-построения. В этой статье мы рассмотрим проблему, её причину и предложим несколько решений.
Проблема
Основная проблема заключается в том, что уничтлажение объекта TThreadPool сталкивается с ограничениями, связанными с функцией FreeLibrary. В частности, если библиотека загружается и затем выгружается, приложение может зависнуть на этапе вызова FreeLibrary(). Это происходит из-за того, что метод TThreadPool.Destroy не всегда корректно обрабатывает ситуацию выгрузки библиотеки.
Пример кода, который демонстрирует проблему:
program ThreadPool;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Winapi.Windows,
System.Threading;
procedure DoSomeTasksFromDll();
var
lHandle: Cardinal;
lProc : procedure;
begin
Writeln('Calling LoadLibrary()');
lHandle := LoadLibrary('threadlib.dll');
Writeln('LoadLibrary() = ', lHandle);
Writeln('Calling GetProcAddress()');
lProc := GetProcAddress(lHandle, 'DoSomeTasks');
Writeln('GetProcAddress() = ', NativeInt(@lProc));
Writeln('Calling DoSomeTasks()');
lProc();
WriteLn('DoSomeTasks() done');
WriteLn('Calling FreeLibrary()');
FreeLibrary(lHandle); // App will freeze here forever
WriteLn('FreeLibrary() done');
end;
begin
try
// Has no effect :(
System.IsLibrary := true;
WriteLn('System.IsLibrary: ', System.IsLibrary);
WriteLn('Current thread: ', GetCurrentThreadId());
DoSomeTasksFromDll();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Writeln('Any key pls');
readln;
end.
Причина проблемы
В методе TThreadPool.Destroy используется флаг IsDLLDetaching, который должен быть установлен в случае выгрузки библиотеки. Однако, если библиотека загружается и выгружается, флаг IsLibrary может быть установлен неправильно, что приводит к зависанию приложения на этапе вызова FreeLibrary().
Решение
Для устранения этой проблемы можно использовать несколько подходов:
Использование собственного DllProc
Один из способов решения проблемы - это использование собственного обработчика событий DLL (DllProc). Этот обработчик должен быть правильно цепочкой к стандартному обработчику RTL, чтобы обеспечить корректное освобождение библиотеки. Пример:
function MyDllProc(dwReason: DWord): Bool; stdcall;
begin
Result := True;
if dwReason = DLL_PROCESS_DETACH then
begin
// Ваш код для освобождения ресурсов
end;
end;
procedure SetCustomDllProc;
begin
OldDllProc := GetProcAddress(GetModuleHandle('rtl.bpl'), 'DLLProc');
if Assigned(OldDllProc) then
begin
SetDLLProc(@MyDllProc);
end;
end;
procedure DoSomeTasksFromDll();
var lHandle: Cardinal;
lProc : procedure;
begin
Writeln('Calling LoadLibrary()');
lHandle := LoadLibrary('threadlib.dll');
Writeln('LoadLibrary() = ', lHandle);
begin try SetCustomDllProc; WriteLn('System.IsLibrary: ', System.IsLibrary);
WriteLn('Current thread: ', GetCurrentThreadId());
DoSomeTasksFromDll();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Writeln('Any key pls');
readln;
end. ```
Использование патчей или обновлений от Embarcadero
Если проблема связана с багом в RTL, то можно ожидать, что Embarcadero выпустит обновление, которое исправит эту проблему. В этом случае лучше всего будет дождаться официального обновления и использовать его.
Избегание использования TThreadPool в DLL
Если проблема не может быть решена, можно рассмотреть возможность избегания использования TThreadPool в DLL. Вместо этого можно использовать другие механизмы управления потоками, которые не зависят от RTL.
Заключение
Проблема с освобождением библиотеки в пуле потоков в Delphi 11 Update 3 может быть решена с помощью использования собственного обработчика событий DLL или ожидания официального обновления от Embarcadero. Важно помнить, что использование собственного обработчика событий DLL требует тщательной настройки и тестирования, чтобы избежать других проблем с управлением ресурсами.
Надеюсь, что эта статья поможет вам решить проблему с освобождением библиотеки в пуле потоков в Delphi 11 Update 3. Если у вас есть дополнительные вопросы или предложения, пожалуйста, не стесняйтесь задавать их в комментариях.
В Delphi 11 Update 3 обнаружена проблема с корректным освобождением библиотек в пуле потоков (TThreadPool), особенно при использовании BPL-построения, приводящая к зависанию приложения при вызове FreeLibrary().
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.