如何在C++中调用多线程中的非线程安全DLL?

Pat*_*ier 5 c++ windows linker thread-safety

我想与线程(而不是多进程)并行化本机c ++代码,该代码使用不是线程安全的DLL(Compute.dll).

实际上,我有一种可以并行化的循环:

for(int i = 0; i < x; i++) {
    ComputeDLL.DoWork(i); // DLL API Call not thread-safe
}
Run Code Online (Sandbox Code Playgroud)

我找到了一种并行化我的本机代码的方法:克隆并重命名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.
Run Code Online (Sandbox Code Playgroud)

你能告诉我这个解决方案是否有效吗?

在此解决方案中:

  • 编译前必须知道线程数
  • 我有太多重复的代码

还有另一种更清洁的方法来解决我的问题吗?我可以使用LoadLibrary()吗?

谢谢

Nb:我不想使用muli处理,因为在我的实际情况中,我必须将参数中的大数据发送到我的DLL(所以我不想使用文件进行通信,因为我需要性能)而且DoWork是非常快(10毫秒).我希望在没有套接字,Windows消息队列等的情况下轻松地在内存中工作......并且在将来,我可能需要线程之间的自定义同步=>多线程模式对我来说是最好的

Geo*_*rge 4

使用许多 DLL 有几个缺点:

  • 您将遇到重复符号的问题。也就是说,链接器将很难知道函数调用引用了哪个 dll
  • 当 DLL 使用某些共享全局数据时,仍然可能会出现线程问题。
  • 每个线程至少需要一个 DLL,这意味着如果您希望它能与任意数量的线程一起工作,则每次都必须复制 DLL。非常笨重。

重复符号问题的解决方案是对您创建的每个线程使用 LoadLibrary / GetProcAddress:

  • 当线程启动时,您必须调用 LoadLibrary 来加载之前未加载的 DLL
  • 然后使用 GetProcAddress 设置线程局部函数指针
  • 从现在开始,您将必须使用这些线程本地函数指针来调用 DLL 中的函数

当您加载的 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
Run Code Online (Sandbox Code Playgroud)

这里send从单独的副本加载的wsock32.dll将仅指向相同的发送函数,因为 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
Run Code Online (Sandbox Code Playgroud)

附加信息:LoadLibrary 将 dll 加载到调用进程的地址空间。LoadLibrary会记住您何时已经加载了 dll,因此通过使用不同的 dll 名称,您可以强制 loadlibrary 将相同的 dll 加载到进程地址空间中的不同位置。

更好的解决方案是从内存中手动加载 DLL 代码,这样可以省去在磁盘上维护同一 DLL 的不同副本的麻烦。

http://www.joachim-bauch.de/tutorials/loading-a-dll-from-memory/