Разработчики, сталкивающиеся с использованием многопоточности в динамических библиотеках на Delphi, могут столкнуться с рядом проблем, связанных с освобождением памяти и взаимодействием между потоками. Одной из таких проблем является замораживание приложения при попытке освободить динамическую библиотеку, использующую TTask и работающую в многопоточном режиме. Давайте рассмотрим подробнее, как можно решить эту проблему.
Описание проблемы
Пользователь столкнулся с проблемой, когда после вызова метода в динамической библиотеке (DLL), использующего TTask, последующий вызов функции FreeLibrary приводит к замораживанию хост-приложения. При отладке было обнаружено, что программа замирает на строке if TMonitor.Wait(FLock, Timeout) then в методе TLightweightEvent.WaitFor, но отладчик не может войти в функцию TMonitor.Wait.
Пример кода, вызывающего проблему
procedure TTaskTest;
begin
TTask.Run(
procedure
begin
Sleep(300);
end);
end;
exports TTaskTest;
Альтернативный ответ и комментарии
Возможное решение проблемы заключается в отмене всех задач в процедуре DllProc при освобождении библиотеки. Однако это может быть слишком поздно. Также предлагается экспортировать функцию для отмены задач, которую необходимо вызвать перед освобождением библиотеки. Существует вероятность, что проблема уже решена в более новых версиях Delphi. Важно отметить, что возможно вызов FreeLibrary происходит слишком быстро, пока задача все еще выполняется.
Подтвержденный ответ
Проблема была зарегистрирована в системе отслеживания ошибок Embarcadero (RSP-13742 Problem with ITask, IFuture inside DLL) и закрыта с комментарием, что для предотвращения сбоев при использовании ITask или IFuture из DLL необходимо использовать собственный экземпляр TThreadPool вместо стандартного.
Пример решения
Для решения проблемы можно создать собственный экземпляр TThreadPool в DLL и использовать его в методе TTask.Run. Важно не забыть освободить ресурсы TThreadPool перед освобождением библиотеки.
library TestLib;
uses
System.SysUtils,
System.Classes,
System.Threading;
{$R *.res}
VAR
tpool: TThreadPool;
procedure TestDelay;
begin
tpool := TThreadPool.Create;
try
TTask.Run(
procedure begin
Sleep(300);
end,
tpool
);
finally
FreeAndNil(tpool);
end;
end;
exports
TestDelay;
begin
end.
Или создать TThreadPool при загрузке библиотеки и добавить процедуру освобождения, которую нужно вызвать перед освобождением библиотеки.
procedure TestDelay;
begin
TTask.Run(
procedure begin
Sleep(300);
end,
tpool
);
end;
procedure ReleaseThreadPool;
begin
FreeAndNil(tpool);
end;
exports
TestDelay, ReleaseThreadPool;
begin
tpool := TThreadPool.Create;
end.
Использование собственного TThreadPool позволяет избежать проблем, связанных с доступом к общим ресурсам в многопоточной среде и правильно управлять жизненным циклом потоков в динамических библиотеках.
Проблема заключается в том, что при использовании многопоточности в динамических библиотеках на Delphi попытка освободить память может приводить к замораживанию приложения из-за проблем синхронизации и управления потоками.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.