[英]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.