我有Dll从磁盘读取文件并返回它的内容:
mydll.h:
extern "C" __declspec(dllexport) void InitFile3dPoints(wchar_t* i_file);
extern "C" __declspec(dllexport) int GetNumPointsForSurface(int i_surf_index);
extern "C" __declspec(dllexport) void GetPointsForSurface
(double* o_result, int i_resultLength, int i_surf_index);
mydll.cpp:
File3dPoints file_3dPoints;
void InitFile3dPoints(wchar_t* i_file)
{ file_3dPoints = readFile3dObjectFromDisk(i_file) }
int GetNumPointsForSurface(int i_surf_index)
{ return file_3dPoints[i_surf_index].getNumPoints(); }
void GetPointsForSurface(double* o_result, int i_resultLength, int i_surf_index);
{
const int num_points = file_3dPoints[i_surf_index].getNumPoints();
if (num_points < i_resultLength)
return;
for (int i = 0; i < num_points; ++i)
o_result[i] = file_3dPoints[i_surf_index].getPoint(i);
}
client.cs:
IntPtr inst = LoadLibrary("mydll.dll");
InitFile3dPoints(filename);
for (int i = 0; i < n; ++i)
{
int num_points_for_surface = GetNumPointsForSurface(i);
double[] points = new double[num_points_for_surface];
GetPointsForSurface(points, points.Length, i);
// some code
}
FreeLibrary(inst);
我的dll不是线程安全的。一个线程可以调用InitFile3dPoints。在调用GetPointsForSurface之前,另一个线程可以调用InitFile3dPoints。 你能不能告诉我如何使它成为线程安全的?创建用于访问file_3dPoints的互斥锁无法解决问题,我需要mydll.cpp中的每个线程都有它的file_3dPoints副本。
由于
答案 0 :(得分:5)
有很多选择可以做到这一点。
首先也是最重要的是不使用全局变量,它们是一场噩梦(出于这个原因和其他原因)。让我们开始更改InitFile3dPoints
签名以分配所需的内存并将其返回给调用者(因此该地址可以用作“句柄”):
File3dPoints* InitFile3dPoints(const wchar_t* i_file)
{
return readFile3dObjectFromDisk(i_file);
}
请注意readFile3dObjectFromDisk
必须返回类型为File3dPoints
的堆分配对象。
然后更改每个函数以接受该指针:
int GetNumPointsForSurface(const File3dPoints* data, int i_surf_index)
{
return *data[i_surf_index].getNumPoints();
}
File3dPoints*
可以在C#中使用IntPtr
封送:
IntPtr inst = LoadLibrary("mydll.dll");
IntPtr data = InitFile3dPoints(filename);
最后,不要忘记在解除分配数据时添加DisposeFile3dPoints
函数:
void DisposeFile3dPoints(File3dPoints* data)
{
if (data != NULL)
delete data;
}
一般来说,为了使DLL“线程安全”(根据您的问题的上下文),您应该使每个函数自包含(它需要的所有数据来自其参数,它没有任何本地静态变量也不是全局的)。请注意,这并没有在任何更广泛的意义上使其真正的线程安全(例如,如果你将一个写入函数暴露给该数据,那么你仍然需要保护访问,但它可以更容易地在C#方面。
您可以更好地将所有这些功能包装在C#类中,用户甚至不会看到:
public class File3D : IDisposable
{
public File3D(string path)
{
// Initialize. Call InitFile3dPoints
}
~File3D()
{
// Call Dispose(false)
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private IntPtr _data;
private void Dispose(bool disposing)
{
// Unmanaged resource, ignore disposing parameter
// and call DisposeFile3dPoints
}
}
答案 1 :(得分:2)
使用互斥锁保护对全局变量的访问。更好的是,不要使用全局变量。
答案 2 :(得分:0)
重新组织代码,以便InitFile3dPoint()
返回File3dPoints
,然后将其作为上下文传递给其他函数。典型的方法是返回指向动态分配的实例的指针。然后,您必须处理正确的所有权和解除分配管理,但即使没有任何互斥锁,您也可以使DLL本身是线程安全的。