I want to parallelize with threads (not with multi-process) a native c++ code which use a DLL (Compute.dll) which is not thread-safe .
Actually, I have a kind of loop that I can parallelize :
for(int i = 0; i < x; i++) {
ComputeDLL.DoWork(i); // DLL API Call not thread-safe
}
I identify a way to parallelize my native code : Clone and rename Compute.dll in Compute1.dll, Compute2.dll, ..., ComputeN.dll and use one dll by thread. So for the link, in the same way, i have to duplicate Compute.lib in Compute1.lib, Compute2.lib, ..., ComputeN.lib
With this solution, I have to duplicate code in my app to define multiple ComputeDLL class : ComputeDLL1, ComputeDLL2, ... ComputeDLLN with an explicit static link :
#pragma comment(lib,'Compute1.lib'); // for ComputeDLL1.h
#pragma comment(lib,'Compute2.lib'); // for ComputeDLL2.h
etc.
Can you tell me if this solution will work ?
In this solution :
Is there an another cleaner good way to solve my problem ? May I use LoadLibrary() ?
Thanks
Nb : I don't want to use muli-processing because in my real case, I have to send big data in parameter to my DLL (so i don't want to use files for communication because I need performance) and a DoWork is very fast (10 ms). I want to easily work in memory without sockets, windows messages queues, etc... and in the future, i will perhaps need custom synchronization between threads => multi-threading pattern is the best for me
Using many DLLs has several drawbacks:
A solution to the duplicate symbols problem is using LoadLibrary / GetProcAddress for every thread you create:
This can be a bad idea when the DLL you load will load another DLL. In this case wsock32 loads ws2_32.dll. Problematic:
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
Here the send
loaded from separate copies of wsock32.dll
will just point to the same send function, because wsock32.dll is just a trampoline for ws2_32.dll.
However, you get different entry points for send, when you load 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
Additional info: LoadLibrary loads a dll to the address space of the calling process. LoadLibrary remembers when you have already loaded a dll, so by using different dll names you can force loadlibrary to load the same dll to different places in the process address space.
A better solution would be to manually load the DLL code from memory, it can spare you the trouble of maintaining different copies of the same dll on disk.
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
This will lead to duplicate symbols on linking stage.
But yes, your idea can be implemented using dynamic loading of dlls via LoadLibrary()
for each thread separately and dynamic resolving of library functions via GetProcAddress()
. You have to have different dll file for each thread (with different names), as you suggested above.
The process approach is the simplest, but you said you had a lot of data to transert. Since you have control over the caller, let me try an answer that involves IPC (inter process communication). There are multiple ways to do that .
Here is an example of how you would work with named pipes.
Serialization comes with it own set of problems, but you can play it fast and loose if you can assume that both process are on the same host and are the same bitness (both 32 bits or both 64 bits). If you cannot make that assumption, try protocol buffers .
With that approach, the caller will create a process, passing that process rendez-vous :
wrapper.exe "\\.\wrapper_1"
wrapper.exe "\\.\wrapper_2"
wrapper.exe "\\.\wrapper_3"
To call instance 1 of the DLL, you would simply serialize your data and write it to \\\\.\\wrapper_1
.
When I had to do this, I recreated the DLL's interface in code so I could replace the wrapper by the real DLL with a #define
statement and a rebuild. It will help debugging the caller, and isolate IPC problems.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.