Synchronize () вешает поток - PullRequest
       19

Synchronize () вешает поток

6 голосов
/ 22 сентября 2010

Я пишу библиотеку DLL в Delphi с несколькими созданными ею потоками. Позвольте мне описать проблему шаг за шагом. Прошу прощения за длинное описание заранее: - (.

Давайте на время забудем о библиотеке. Я создал приложение для Windows, которое будет представлять виды с нескольких камер. Я создал окно, которое предназначено для отображения вида с одной камеры и содержит элемент управления TImage. Существует поток (потомок TThread), который загружает текущее изображение с камеры каждые пару миллисекунд и назначает его элементу управления TImage этого окна (используя метод Synchronize ()). Приложение создает несколько экземпляров этого окна при запуске (с отдельным потоком для каждого из них), поэтому вы можете видеть в режиме реального времени несколько камер одновременно. Более того, все эти окна просмотра объединены главным окном приложения, поэтому они появляются внутри него.

Все работало нормально, пока я не решил поместить эти два окна в библиотеку dll. Я просто нашел это необходимым по некоторым причинам, но они не важны сейчас. Поэтому я создал новую библиотеку dll, добавил в проект существующее главное окно и окно просмотра камеры и экспортировал функцию, которая создает и возвращает экземпляр главного окна. Когда главное окно создано, оно создает несколько окон просмотра камеры, делая себя их родителем.

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

Когда я запустил приложение, выяснилось, что я не мог получить ни одного изображения с любой камеры. Когда я его отладил, я заметил, что когда поток вызывает метод Synchronize (), он зависает навсегда. Этого не произошло до того, как поместить оба этих окна в dll.

И это моя проблема. Честно говоря, это мой первый подход к библиотекам, с которым мне пришлось столкнуться во многих других проблемах. Вы можете задаться вопросом, почему я использую окна вместо фреймов ... Поэтому всякий раз, когда я создавал экземпляр TFrame в dll, я получал исключение, говорящее "у элемента управления xxx нет родительского окна". Я не знал, что с этим делать, поэтому вместо этого использовал windows: - (.

Подскажите, пожалуйста, что делать с проблемой синхронизации? Основной поток, похоже, не блокируется каким-либо образом при запуске приложения, поскольку он принимает нажатия кнопок и т. Д. В чем проблема?

Пожалуйста, помогите!

Заранее спасибо !!

Ответы [ 3 ]

13 голосов
/ 22 сентября 2010

Когда вы вызываете TThread.Synchronize, указатель потока и метода добавляется к глобальному SyncList: TList в Classes.pas. В основной программе TApplication.Idle обычные вызовы CheckSynchronize, которая проверяет SyncList, но собирается проверить версию в исполняемой программе, а не версию в DLL. Конечный результат, ваши синхронизированные методы никогда не вызываются.

Самым простым решением было бы переключиться с DLL на пакеты, что позволило бы устранить дубликат SyncList.

Другой подход заключается в переопределении обратного вызова Application.OnIdle в exe-файле и вызове CheckSynchronize вашей DLL вручную. Для этого вам потребуется некоторая помощь со стороны приложения, поскольку ваша DLL также будет иметь объект Application, и этот объект не будет работать.

2 голосов
/ 22 сентября 2010

Это плохая идея использовать Synchronize, потому что это приводит к подобным условиям гонки. Я не знаю, что конкретно происходит в вашем коде - трудно увидеть, не видя никакого кода, - но на самом деле такого рода проблемы довольно распространены.

Связь между потоками лучше выполнять с помощью очереди. Если у вас последняя версия, Delphi XE, в Generics.Collections есть класс TThreadedQueue<T>, который идеально подходит для такого рода вещей. Передайте 0 параметру PopTimeout в конструкторе, попросите потоки камеры выдавать изображения, а основной поток опрашивает очереди с третьей перегрузкой PopItem, например:

var
  CurrentItem: TImage;
begin
  if ThreadQueue.PopItem(CurrentItem) = wrSignaled then
    UpdateImage(CurrentItem); //or however you do it
end;

(Если в очереди ничего нет, PopItem вместо этого вернет wrTimeout.)

Если у вас нет Delphi XE, вам нужно создать собственную очередь для потоковой защиты или найти ее из стороннего источника, такого как OmniThreadLibrary Примоза Габриэльчича.

0 голосов
/ 22 января 2016

Я нашел два способа решения Synchronize() зависания потока (в Delphi 7):

  1. Поместите TTimer в форму Dll и onTimer вызов события CheckSynchronize;

procedure TPluginForm.Timer1Timer(Sender: TObject); begin CheckSynchronize; end;

  1. Добавить этот модуль в раздел с использованием формы Dll
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...