Synchronize() 挂起线程

发布于 2024-09-24 08:52:48 字数 912 浏览 2 评论 0原文

我正在 Delphi 中编写一个 dll 库,其中包含由它创建的多个线程。让我一步步描述这个问题。我很抱歉提前进行了冗长的描述:-(。

让我们暂时忘记这个库。我创建了一个 Windows 应用程序,它将显示来自多个摄像机的视图。我创建了一个窗口,用于显示来自多个摄像机的视图。一个相机,它包含一个 TImage 控件。有一个线程(TThread 后代)每隔几毫秒从相机下载当前图像并将其分配给该窗口的 TImage 控件(使用 Synchronize() 方法)。应用程序在启动时创建该窗口的多个实例(每个实例都有一个单独的线程),因此您可以同时看到多个摄像机的实时视图,而且所有这些查看窗口都是主应用程序窗口的父级。 一切都工作正常,

直到我决定将这两个窗口放入 dll 库中,我只是发现由于某些原因有必要,但它们现在并不重要,所以我创建了一个新的 dll 库,添加了现有的主窗口。并将相机视图窗口添加到项目中,并导出一个创建并返回主窗口实例的函数。创建主窗口时,它会创建几个相机视图窗口,使其成为它们的父窗口。

然后,出于测试目的,我创建了一个应用程序,从库中导入上述 dll 函数,并在启动时调用它以获取主窗口的实例;然后将其显示在屏幕上(处于非模态状态)。

当我启动该应用程序时,发现我无法从任何相机获取单个图像。当我调试它时,我注意到当线程调用 Synchronize() 方法时,它会永远挂起。在将这两个窗口放入 dll 之前,这种情况并没有发生。

这是我的问题。老实说,这是我第一次接触图书馆,到目前为止我还必须解决许多其他问题。你可能想知道为什么我使用窗口而不是框架......所以每当我在 dll 中创建 TFrame 的实例时,我都会得到一个异常,说“控件 xxx 没有父窗口”。我不知道该怎么办,所以我使用了 Windows :-(。

您能告诉我如何处理同步问题吗?当应用程序启动时,主线程似乎没有以任何方式被阻止接受点击按钮等。那么有什么问题吗?

请帮忙

I'm writing a dll library in Delphi with multiple threads created by it. Let me describe the problem step by step. I'm sorry for a lengthy description in advance :-(.

Let's forget about the library for a while. I created a windows application that is going to present views from several cameras. I created a window which is meant to show the view from a single camera and it contains a TImage control. There is a thread (a TThread descendant) that downloads the current image from the camera every couple of milliseconds and assigns it to the TImage control of that window (using the Synchronize() method). The application creates several instances of that window on startup (with a separate thread for each of them), so you can see the live view from several cameras at once. What's more, all those viewing windows are parented by the main application window, so they appear within it.

Everything worked fine until I decided to put those two windows into a dll library. I just found it necessary for some reasons, but they are not important now. So I created a new dll library, added the existing main window and the camera-view window to the project and exported a function that creates and returns an instance of the main window. When the main window is created, it creates several camera-view windows, making itself their parent.

Then, for testing purposes, I created an app that imports the above mentioned dll function from the library and calls it at startup to get an instance of the main window; then just shows it on the screen (in a non-modal state).

When I started the app it came out that I couldn't get a single image from any camera then. When I debugged it, I noticed that when the thread calls the Synchronize() method, it hangs forever. It didn't happen before putting both those windows into a dll.

And this is my problem. To be honest, this is my first approach to libraries I have had to get through many other problems so far. You might wonder why I use windows instead of frames... So whenever I created an instance of a TFrame in a dll, I would get an exception saying "the control xxx does not have a parent window". I did not know what to do about that so I used windows instead :-(.

Could you please tell me what to do with the synchronization problem? The main thread does not seem to be blocked in any way when the application is started for it accepts clicking buttons etc. What is the problem then?

Please, help!

Thank you in advance!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦冥 2024-10-01 08:52:48

当您调用 TThread.Synchronize 时,线程和方法指针将添加到 Classes.pas 中的全局 SyncList: TList 中。在主 exe 的 TApplication.Idle 例程中调用 CheckSynchronize,它检查 SyncList,但它将检查 exe 中的版本而不是DLL 中的一个。最终结果是,您的同步方法永远不会被调用。

最简单的修复方法是从 DLL 切换到包,这将消除重复的 SyncList

另一种方法是覆盖 exe 的 Application.OnIdle 回调,并手动调用 DLL 的 CheckSynchronize。不过,您需要应用程序的一些帮助,因为您的 DLL 也将有一个 Application 对象,而该对象将无法工作。

When you call TThread.Synchronize the thread and method pointer are added to a global SyncList: TList in Classes.pas. In the main exe's TApplication.Idle routine calls CheckSynchronize, which checks the SyncList, but it's going to check the version in the exe instead of the one in the DLL. End result, your synchronized methods are never called.

The easiest fix would be to switch from DLLs to packages, which would eliminate the duplicate SyncList.

Another approach would be to override the exe's Application.OnIdle callback, and call your DLL's CheckSynchronize manually. You would need some help from the application for that though, since your DLL will have an Application object too, and that one won't work.

我的痛♀有谁懂 2024-10-01 08:52:48

使用同步不是一个好主意,因为它往往会导致这样的竞争条件。我不知道你的代码中具体发生了什么——在没有看到任何代码的情况下很难判断——但这类问题实际上很常见。

线程间通信最好使用队列来完成。如果您拥有最新版本 Delphi XE,Generics.Collections 中有一个 TThreadedQueue 类,它非常适合此类操作。将 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 的 OmniThreadLibrary。

It's a bad idea to use Synchronize, because it tends to lead to race conditions like this. I don't know what's going on specifically in your code--it's hard to tell without seeing any code--but this sort of issue is pretty common actually.

Inter-thread communication is better done with a queue. If you've got the latest version, Delphi XE, there's a TThreadedQueue<T> class in Generics.Collections that's ideal for this sort of thing. Pass 0 to the PopTimeout param in the constructor, have your camera threads push images, and have your main thread poll the queues with the third PopItem overload, like so:

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

(If there's nothing in the queue, PopItem will return wrTimeout instead.)

If you don't have Delphi XE, you'll need to build your own threadsafe queue, or find one from a third party source, such as Primoz Gabrielcic's OmniThreadLibrary.

风透绣罗衣 2024-10-01 08:52:48

我找到了两种方法来解决 Synchronize() 挂起线程(在 Delphi 7 中):

  1. 在 DLL 窗体上放置一个 TTimer 并使其具有 OnTimer 事件调用CheckSynchronize();

    过程 TPluginForm.Timer1Timer(Sender: TObject);
    开始
      检查同步;
    结尾;
    
  2. 将此模块添加到 DLL 表单的 uses 部分< /a>


I found two ways to solve Synchronize() hanging up the thread (in Delphi 7):

  1. Place a TTimer on the DLL form and have its OnTimer event call CheckSynchronize();

    procedure TPluginForm.Timer1Timer(Sender: TObject);
    begin
      CheckSynchronize;
    end;
    
  2. Add this module to the uses section of the DLL form

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文