Подтвердить что ты не робот

Что такое TExternalThread? "Не удается завершить созданный извне поток" при завершении потокового таймера

Это происходит в половине случаев закрытия моего приложения, в которое я поместил TLMDHiTimer в мою форму во время разработки, Enabled установлен в true. В моем событии OnFormClose я вызываю MyLMDHiTimer.Enabled: = false. Когда это вызывается, я иногда (примерно в половине случаев) получаю это исключение.

Я отлаживал и входил в вызов и обнаружил, что это строка 246 в LMDTimer.pas, которая дает эту ошибку.

FThread.Terminate;

Я использую последнюю версию LMDTools. Я сделал полную переустановку инструментов LMD до уик-энда и удалил и снова добавил компонент в форму правильно.

Из того, что я нашел, это имеет какое-то отношение к TExternalThread, но там нет документации от Embarcadero, и я не нашел ничего, ссылающегося на него в исходном коде LMDTools.

Использование полностью обновленного RAD Studio 2010, Delphi 2010.

Что меня действительно расстраивает, так это отсутствие документации. Результат Google yeilds один, который на самом деле говорит об этом, в котором кто-то говорит, что ошибка вызвана попыткой прервать TExternalThread. Но, глядя на исходный код этого LMDHiTimer, он ни разу не пытается сделать что-либо, кроме создания обычного TThread. Один результат Google, который я смог найти, Тема: Не удается завершить созданный извне поток? на Embarcadero упоминает, используя GetCurrentThread() и GetCurrentThreadId(), чтобы получить данные, необходимые для подключения к существующему потоку, но TLMDHiTimer не делает этого. Он просто создает собственный потомок TThread со своим собственным конструктором Create() (переопределенный, конечно, и вызовы, унаследованные в начале конструктора)

Итак... Какого черта это TExternalThread? Кто-нибудь еще сталкивается с подобным исключением? И, возможно, выяснили решение или обходное решение? Я попросил почти тот же самый вопрос для собственной поддержки LMDTools, но он не может повредить спросить в нескольких местах.

Заранее благодарим за любую помощь.

4b9b3361

Ответ 1

TExternalThread обертывает поток, который Delphi RTL не создавал. Он может представлять собой поток, принадлежащий пулу потоков ОС, или, возможно, поток, созданный другой DLL в вашей программе. Поскольку поток выполняет код, который не относится к связанному классу TExternalThread, метод Terminate не имеет способа уведомить поток, который вы хотите остановить.

Объект Delphi TThread установит для свойства Terminated значение True, и ожидается, что метод Execute, который получил переопределение, будет проверять это свойство периодически, но поскольку этот поток не является кодом Delphi, не существует метода Execute, а любой Terminated свойство появилось только после того, как код потока уже был написан где-то еще (не путем переопределения Execute).

В ленте новостей видно, что, вероятно, происходит в вашем случае:

... у вас есть поврежденная память, которая заставляет член TThread.FExternalThread стать ненулевым значением.

Возможно, это связано с ошибкой в ​​библиотеке компонентов, или это может быть связано с ошибкой в ​​вашем собственном коде. Вы можете использовать контрольные точки данных отладчика, чтобы попытаться выяснить это. Установите точку останова в конструкторе потока таймера. Когда ваша программа приостанавливается, используйте команду "Добавить точку останова" в меню "Выполнить", чтобы добавить точку останова данных, используя адрес нового поля FExternalThread объекта. После этого, если это значение поля изменится, отладчик остановится и покажет вам, что изменило его. (Контрольная точка данных будет получать reset каждый раз, когда вы запускаете программу, потому что IDE предполагает, что объект не будет распределяться по одному и тому же адресу каждый раз.)

Ответ 2

Есть ли вероятность, что код может попытаться завершить уже разрушенный TThread? Это может легко произойти, если у вас установлен FreeOnTerminate.

Я заметил ваш пост при диагностике аналогичной (противоположной?) ошибки, "Не могу вызвать" Начать в запущенном или приостановленном потоке "в конструкторе компонента, помещенного в основную форму. Когда я удалил Start(), эта ошибка была заменена более значительными ошибками, например." неправильная операция указателя "и" нарушение доступа "в соответствующем деструкторе. Компонент пытался манипулировать своим TThread-объектом после того, как TThread был Freed, тем самым оставив все в соответствии с законом Мерфи. Когда я исправил это, я смог заменить вызов Start(), не возвращая ошибку" Can not call Start".

По аналогии, может ли ваша проблема быть в том, что адрес вашего FExternalThread был переработан и сбит до вызова деструктора/завершения? В нашем случае у нас была некорректная реализация Singleton Instance Pattern; но опять же, FreeOnTerminate также кажется вероятным подозреваемым.

[FYI: Я использую Я использую С++ под RAD Studio XE]

Ответ 3

Проблема заключается в том, что FExternalThread не инициализируется значением false по умолчанию при создании экземпляра TThread. Таким образом, вы не можете гарантировать, какое значение должно быть в этой переменной (иногда это может быть true, иногда false).

Ответ 4

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

Наконец, я купил хорошую систему исключений (EurekaLog), и это привело меня непосредственно к стороннему компоненту, который вызывал проблему. Этот компонент, TAbLED, обладает свойством устанавливать его для вспышки с какой-то временной синхронизацией. Устранение этой проблемы не решило проблему.