如何创建多线程 Dll

Posted

技术标签:

【中文标题】如何创建多线程 Dll【英文标题】:How to create Multithreaded Dll 【发布时间】:2013-06-03 12:11:19 【问题描述】:

我有从磁盘读取文件并返回其内容的 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 的副本。

谢谢

【问题讨论】:

【参考方案1】:

有很多选项可以做到这一点。

首先,最重要的是不要使用全局变量,它们是一场噩梦(出于这个和其他原因)。让我们开始更改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
    

【讨论】:

非常感谢您的回答!很好的解释! 很好的答案,解决了我长期存在的问题。谢谢!【参考方案2】:

使用互斥锁保护对全局变量的访问。更好的是,不要使用全局变量。

【讨论】:

或者如果可能的话使用 std::atomic 不要使用 std::atomic 也不要使用互斥锁。只需停止使用全局变量。如果可能,应避免使用锁。它们阻碍了扩展。所以,在这种不需要锁的情况下,不要使用它们!【参考方案3】:

重新组织您的代码,使InitFile3dPoint() 返回一个File3dPoints,然后您将其作为上下文传递给其他函数。典型的方法是返回一个指向动态分配实例的指针。然后,您必须注意适当的所有权和解除分配管理,但即使没有任何互斥体,您也会使 DLL 本身成为线程安全的。

【讨论】:

以上是关于如何创建多线程 Dll的主要内容,如果未能解决你的问题,请参考以下文章

多线程 dll 和 lock whan 从第二个线程调用函数

VB 6 多线程混淆

visual studio2010的项目属性设置中,运行库选择多线程,多线程调试,多线程dll,多线程调试dll有啥区别

boost.python 真正的多线程

我的 MFC C++ .dll 的多线程

dll中的多线程