[英]How to call not-thread safe DLL in multi-thread in C++?
我想与线程(而不是多进程)并行化本机c ++代码,该代码使用不是线程安全的DLL(Compute.dll)。
实际上,我有一种可以并行化的循环:
for(int i = 0; i < x; i++) {
ComputeDLL.DoWork(i); // DLL API Call not thread-safe
}
我找到了一种并行化我的本机代码的方法:克隆并重命名Compute1.dll中的Compute.dll,Compute2.dll,...,ComputeN.dll并通过线程使用一个dll。 所以对于链接,以同样的方式,我必须复制Compute1.lib中的Compute.lib,Compute2.lib,...,ComputeN.lib
有了这个解决方案,我必须在我的应用程序中复制代码以定义多个ComputeDLL类:ComputeDLL1,ComputeDLL2,...带有显式静态链接的ComputeDLLN:
#pragma comment(lib,'Compute1.lib'); // for ComputeDLL1.h
#pragma comment(lib,'Compute2.lib'); // for ComputeDLL2.h
etc.
你能告诉我这个解决方案是否有效吗?
在此解决方案中:
还有另一种更清洁的方法可以解决我的问题吗? 我可以使用LoadLibrary()吗?
谢谢
Nb:我不想使用muli处理,因为在我的实际情况中,我必须将参数中的大数据发送到我的DLL(所以我不想使用文件进行通信,因为我需要性能)而且DoWork是非常快(10毫秒)。 我希望在没有套接字,Windows消息队列等的情况下轻松地在内存中工作......并且在将来,我可能需要线程之间的自定义同步=>多线程模式对我来说是最好的
使用许多DLL有几个缺点:
重复符号问题的解决方案是为您创建的每个线程使用LoadLibrary / GetProcAddress:
当您加载的DLL将加载另一个DLL时,这可能是一个坏主意。 在这种情况下,wsock32加载ws2_32.dll。 问题:
In [1]: import ctypes
In [2]: k=ctypes.windll.kernel32
In [3]: a=k.LoadLibraryA('wsock32_1.dll')
In [4]: b=k.LoadLibraryA('wsock32_2.dll')
In [5]: a
Out[5]: 1885405184
In [6]: b
Out[6]: 1885339648
In [7]: k.GetProcAddress(a, "send")
Out[7]: 1980460801
In [8]: k.GetProcAddress(b, "send")
Out[8]: 1980460801
这里从wsock32.dll
单独副本加载的send
只会指向相同的发送函数,因为wsock32.dll只是ws2_32.dll的蹦床。
但是,当您加载ws2_32时,您将获得不同的发送入口点。
In [1]: import ctypes
In [2]: k=ctypes.windll.kernel32
In [3]: a=k.LoadLibraryA('ws2_32_1.dll')
In [4]: b=k.LoadLibraryA('ws2_32_2.dll')
In [5]: a
Out[5]: 1874853888
In [6]: b
Out[6]: 1874591744
In [7]: k.GetProcAddress(a, "send")
Out[7]: 1874882305
In [8]: k.GetProcAddress(b, "send")
Out[8]: 1874620161
附加信息:LoadLibrary将dll加载到调用进程的地址空间。 LoadLibrary会记住您已加载dll的时间,因此通过使用不同的dll名称,您可以强制loadlibrary将相同的dll加载到进程地址空间中的不同位置。
更好的解决方案是从内存中手动加载DLL代码,它可以省去在磁盘上维护相同dll的不同副本的麻烦。
http://www.joachim-bauch.de/tutorials/loading-a-dll-from-memory/
#pragma comment(lib,'Compute1.lib'); // for ComputeDLL1.h
#pragma comment(lib,'Compute2.lib'); // for ComputeDLL2.h
这将导致链接阶段出现重复符号。
但是,您可以通过LoadLibrary()
为每个线程单独动态加载dll并通过GetProcAddress()
动态解析库函数来实现您的想法。 如上所述,您必须为每个线程(具有不同的名称)使用不同的dll文件。
过程方法是最简单的,但是你说你有很多要转换的数据。 由于您可以控制调用者,让我尝试一个涉及IPC(进程间通信)的答案。 有多种方法可以做到这一点 。
以下是如何使用命名管道的示例。
序列化,用它自己的问题,但你可以发挥快速和松散的,如果你可以假定这两个过程都在同一台主机上,并同位数 (32位或两个64位)。 如果你不能做出这个假设,试试协议缓冲区 。
使用这种方法,调用者将创建一个进程,将该进程传递给rendez-vous :
wrapper.exe "\\.\wrapper_1"
wrapper.exe "\\.\wrapper_2"
wrapper.exe "\\.\wrapper_3"
要调用DLL的实例1,您只需序列化数据并将其写入\\\\.\\wrapper_1
。
当我不得不这样做时,我在代码中重新创建了DLL的接口,因此我可以使用#define
语句和重建来替换真实DLL的包装器。 它将帮助调试调用者,并隔离IPC问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.