【问题标题】:Is it possible to start (and stop) a thread inside a DLL?是否可以在 DLL 中启动(和停止)线程?
【发布时间】:2011-11-26 00:25:31
【问题描述】:

我正在考虑为我所做的一些常见事情构建 DLL 的一些想法。我想检查的一件事是否可能是在 DLL 中运行线程。我确信我至少可以启动它,并让它在终止时自动释放(并使其强制终止自身)——我认为这不是什么大问题。但是一旦我启动它,我看不到如何继续与它通信(尤其是停止它),主要是因为对 DLL 的每次调用都是唯一的(据我所知),但我对主题。

我已经看到在某些情况下,DLL 可以在开始时加载并在不再需要时在结束时释放。我对这种方法的知识或经验为 0,除了看到与它相关的东西,甚至不能告诉你什么或如何,我不记得了。但这有可能吗?

我知道 ActiveX/COM,但这不是我想要的 - 我只想要一个可以跨语言(特别是 C#)使用的基本 DLL。

另外,如果可能的话,我将如何进行从 DLL 到应用程序的回调?例如,当我启动线程时,我很可能会分配一个函数(在 EXE 中)作为事件(从 DLL 触发)的处理程序。

所以我想我要问的是 - 如何加载 DLL 以进行连续工作并在完成后释放它 - 而不是根据需要调用 DLL 中的各个函数的简单方法。在同样的情况下——我可能会在 DLL 中分配变量或创建对象。如何确保一旦分配了该变量(或创建了对象),如何确保下次调用 DLL 时该变量或对象仍然可用?显然,它需要一种初始化/完成 DLL 的机制(即在加载 DLL 时在 DLL 中创建对象,并在卸载 DLL 时释放对象)。

编辑: 最后,我将 DLL 包装在一个组件中,因此当创建组件的实例时,将加载 DLL 并在 DLL 中创建相应的线程,然后当组件被释放时,DLL 被卸载。还需要确保如果这些组件中有 2 个,那么每个组件都会加载 2 个 DLL 实例。

这是否与 IInterface 的使用有关?因为我也有这方面的 0 经验。

无需直接用示例源代码回答这个问题 - 一个好的教程的链接会很棒。

【问题讨论】:

  • 最后一点,我的意思是我不希望任何人花时间详细回答这个问题,我知道这是一个很大的话题,我需要的只是有人给我看一个教程这样做,而不使用 ActiveX。
  • 启动无法与之通信和控制的线程是个坏主意。
  • 确实,这就是为什么我要在他们开始之后弄清楚如何与他们交流。
  • 我找到了这个网页:delphi3000.com/articles/article_1366.asp?SK=,它准确地描述了我想要找出的内容(加载/卸载 dll)——但是这个线程安全吗?
  • 如果不描述你的线程模型,线程安全本身是没有意义的。每个实例一个线程?一个全局实例?您的问题真的是关于如何使用 LoadLibrary 吗?如果是这样,请不要依赖那篇文章。

标签: delphi dll delphi-7


【解决方案1】:

你正在寻找的基本模式是这样的:

  1. EXE 调用 DLL 中的函数。
  2. EXE 根据需要传递参数。
  3. DLL 执行工作,启动线程,返回句柄。我所说的句柄是指松散意义上的作品,而不是指 Windows 句柄。
  4. EXE 现在可以通过调用更多函数、传递句柄与 DLL 进一步通信。
  5. DLL 可以与 EXE 通信,例如通过回调,只要 EXE 提供了回调。
  6. 当需要停止时,EXE 调用一个 DLL 函数,传递句柄。至此,DLL 整理完毕。

句柄后面是 DLL 需要在调用之间跟踪的任何状态。从 EXE 的角度来看,这个句柄是不透明的。

这种模式即使使用非 OOP 语言(如 C)也可以正常工作。但是,使用 OOP 工具要容易得多。由于您希望跨越模块边界,因此使用接口而不是 Delphi 对象会更安全。如果跨模块边界传递 Delphi 对象,则只能传递对象的数据部分,而不能传递代码部分。

使用接口时,DLL 返回的接口变量用作上面我编号列表中的句柄。它包含任何实例数据。使用接口可以实现现成的可扩展性。

至于线程部分,最好使用更高级别的线程库而不是原始线程。我推荐的图书馆是 Primož Gabrijelčič 的 OmniThreadLibrary

关于您的编辑:

我会把DLL封装在一个组件里面,所以当一个组件的实例被创建时,DLL会被加载并在DLL内部创建一个对应的线程,然后当组件被释放时,DLL是卸载。还需要确保如果有 2 个这样的组件,那么每个组件都会加载 2 个 DLL 实例。

当您将 DLL 加载到进程中时,它只有一个实例。特别是如果您打算使用全局变量,那么无论您调用多少次LoadLibrary,每个全局变量都只有一个实例。

【讨论】:

  • 所以只是为了确保我理解正确:如果我在 DLL 中声明了一个变量,并且我向设置该变量的 DLL 发送调用,那么下次我在DLL,该变量仍将被分配它是如何上次调用的?我害怕在不知道是否可以先尝试的情况下尝试。
  • 如果你使用全局变量,当然可以。但你不想那样做。尤其是因为你有线程。所以将状态存储在句柄或接口后面的对象中。
【解决方案2】:

我试图将此作为评论发布,但字符用完了:简短的回答是肯定的,应该是可能的。设计 API 使其同步且运行良好是另一回事。我通常使用 TCP/IP 网络肥皂盒进行跨平台/语言通信,但尤其是在您正在寻找异步操作的环境中。设计一个具有“Start”、“Stop”和“isReady”调用的服务并不难。

http://docwiki.embarcadero.com/VCL/en/Classes.TThread

您正在潜入允许竞争条件并需要一些智能检查才能有效的领域。请注意这一点并阅读该主题。

以下是创建 DLL 的基础知识: http://delphi.about.com/od/windowsshellapi/a/dll_basics.htm

这里是使用一个的基础知识。我已经离开 Delphi 好几年了,否则我会举一个简单的例子,我没有为自己购买副本的冲动,但所有语言的原则都是相同的。那里有大量关于线程和接口设计的 Java/C# 教程。

一般来说,“简单”设置可能是:

类 - 对象工作待完成 - 布尔 isRunning - 布尔 isReady - 程序停止; - 程序开始(工作); - 函数 isReady() : boolean;

您的线程将在结构的 while 循环中运行:

procedure start(work) //inside new thread
begin
  isRunning := true;
  isReady := false;

  while(isRunning)
    begin
      doIncrementalOperationOnWork();
      thread.yield;
    end;
  work := thread.getWork();
  isRunning := false;
  isReady := true;

end;

这是一个粗略的草图,但想法是外部调用者可以检查它是否准备好并在准备好时返回。我不确定你在做什么,但这是一个“可能的”解决方案。管理起来很多,正如 Delphi 所说:每个 CPU 16 个线程是它们的“上限”。请注意限制。其他人可能有更好的建议,这只是我用来将工作卸载到看似简单直接的线程上的一种抽象模式。

【讨论】:

  • 一些优点,但不是我想要的答案,谢谢。
  • @DavidHeffernan,来自链接 above 中的文档:'跟踪太多线程会消耗 CPU 时间;建议的限制是单处理器系统上每个进程 16 个活动线程。'
  • 如果您的线程确实可以工作,您希望每个内核有 1 个。如果您的线程正在等待,那么每个内核超过 16 个就可以了,尽管毫无意义。任何限制都与 Delphi 无关,实际上由底层系统决定。如果您在单个 32 位进程中有太多,那么每个内核 1MB 的堆栈储备会迅速耗尽资源。
  • 这里是 Mark Russinovich 的文章Pushing the Limits of Windows: Processes and Threads 的链接,该文章详细测试并解释了 32 位和 64 位 Windows 系统中线程资源和其他 Windows 资源的限制。
【解决方案3】:

大量的问题和充满乐趣的学习体验。

加载和卸载 dll 并不难,您可以添加一个名为 dllMain 的函数。它是 windows 在被要求加载和卸载 dll 时使用的特殊伙伴。更多细节在这里

About Delphi

大量关于 delphi 和线程以及如何在 delphi 中使用接口的文章。

当您遇到一些更具体的问题时,我应该能够提供帮助。然而,作为一个 Delphi 和 C# 程序员,我会用 C# 编写这些东西,为 Com 包装它,然后从 Delphi 调用它。您将获得更多帮助,找到更多教程,并且语言支持(尤其是在复杂场景中)要好得多。

BackgroundWorker 和线程池,更不用说并行扩展使 .net 中的线程变得有趣。在 Delphi 中,这只是痛苦的。

【讨论】:

  • 在Delphi中有很多做线程的人。有许多用 C# 编写的优秀线程库,使用互操作对我来说听起来不是一个好主意。
  • 我也是,但如果我可以选择在 c# 或 Delphi 中使用线程,我知道我会选择哪一个。
  • COM 或什至服务的可接受程度主要取决于必须来回传递多少信息以及可以提供何种程度的解耦。如果它只是 DoTask 并告诉我你什么时候完成并且任务是在 .net 代码中定义的......
【解决方案4】:

线程存在于进程内部。

一个进程运行可执行代码:可以是内置在 .exe 中的代码,也可以是从外部 .dll 加载的代码。

进程还可以“远程”运行代码 - 代码在单独的进程中,甚至是单独的机器 - 使用 DCOM (Microsoft COM/ActiveX)、WCF (Microsoft .Net)、RMI (Java) 或 CORBA/ 等技术IIOP(除其他外)。

由于您在 Delphi 论坛上发帖,最好的选择可能是调查 Delphi Prism:

http://edn.embarcadero.com/article/41598

如果您坚持使用旧版本的 Delphi 7,最好的办法可能是坚持使用原始套接字与远程 PC 进行通信。

【讨论】:

  • 请注意,因为我们是在彼此之上:我不认为我的示例是一个好的设计选择,它是对问题的技术回答。
  • 与远程 PC 的通信问题在哪里?我在两个地方提到我不想故意使用 ActiveX。而且我也不想离开 Delphi7 - 我实际上已经从 Delphi 2010 降级了。
  • -1。这对回答所提出的问题没有任何帮助,该问题没有提及“与远程 PC 通信”,在 Delphi 论坛(这是 StackOverflow)中没有提出任何问题,并且与使用 Prism 而不是 Delphi 无关。问题是关于 Delphi DLL 中的线程。
  • 真的吗?报复性投票?在一个有 30 个赞的旧帖子上?
  • 别难过,上周我让某人浏览了我的每一个帖子,并出于某种愚蠢的原因投了反对票。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多