我正在Delphi中编写一个dll库,它创建了多个线程。让我一步一步地描述问题。我很抱歉提前做了冗长的描述: - (。
让我们暂时忘掉图书馆。我创建了一个Windows应用程序,它将从几个摄像头呈现视图。我创建了一个窗口,用于显示来自单个摄像头的视图,它包含一个TImage控件。有一个线程(一个TThread后代)每隔几毫秒从摄像机下载当前图像,并将其分配给该窗口的TImage控件(使用Synchronize()方法)。应用程序在启动时创建该窗口的多个实例(每个窗口都有一个单独的线程),因此您可以同时从多个摄像头中查看实时视图。更重要的是,所有这些查看窗口都是主应用程序窗口的父级,因此它们出现在其中。
一切正常,直到我决定将这两个窗口放入dll库中。我发现有必要出于某些原因,但现在它们并不重要。所以我创建了一个新的dll库,将现有的主窗口和摄像机视图窗口添加到项目中,并导出了一个创建并返回主窗口实例的函数。创建主窗口时,它会创建几个摄像机视图窗口,使其成为父窗口。
然后,出于测试目的,我创建了一个应用程序,它从库中导入上面提到的dll函数,并在启动时调用它来获取主窗口的实例;然后只是在屏幕上显示它(处于非模态状态)。
当我启动应用程序时,我发现我无法从任何相机中获取单个图像。当我调试它时,我注意到当线程调用Synchronize()方法时,它会永远挂起。在将这两个窗口放入dll之前没有发生过。
这是我的问题。说实话,这是我对图书馆的第一种方法,到目前为止,我必须解决许多其他问题。您可能想知道为什么我使用Windows而不是框架...所以每当我在dll中创建一个TFrame实例时,我会得到一个例外,说“控件xxx没有父窗口”。我不知道该怎么办,所以我改用了windows: - (。
您能否告诉我如何处理同步问题?当应用程序启动时,主线程似乎没有以任何方式被阻止它接受点击按钮等。那么问题是什么?
请帮忙!
提前谢谢!!
答案 0 :(得分:13)
当您调用TThread.Synchronize
时,线程和方法指针将添加到Classes.pas中的全局SyncList: TList
。在主exe的TApplication.Idle
例程调用CheckSynchronize
中,它检查SyncList
,但它将检查exe中的版本而不是DLL中的版本。最终结果,永远不会调用您的同步方法。
最简单的解决方法是从DLL切换到包,这将消除重复的SyncList
。
另一种方法是覆盖exe的Application.OnIdle
回调,并手动调用DLL的CheckSynchronize
。你需要一些来自应用程序的帮助,因为你的DLL也会有一个Application
对象,而且那个对象不起作用。
答案 1 :(得分:2)
使用Synchronize是一个坏主意,因为它往往会导致像这样的竞争条件。我不知道你的代码中有什么特别的东西 - 没有看到任何代码就很难说 - 但实际上这种问题很常见。
使用队列可以更好地完成线程间通信。如果您有最新版本的Delphi XE,那么TThreadedQueue<T>
中的Generics.Collections
类就是这种类型的理想选择。将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,你需要建立自己的线程安全队列,或者从第三方来源找到一个,例如Primoz Gabrielcic's OmniThreadLibrary。
答案 2 :(得分:0)
我找到了两种解决Synchronize()
挂起线程的方法(在Delphi 7中):
TTimer
并onTimer
事件调用CheckSynchronize
; procedure TPluginForm.Timer1Timer(Sender: TObject);
begin
CheckSynchronize;
end;