将遗留 C 代码集成到多线程 C++ 代码中
Integrating legacy C code in multi-threaded C++ code
假设我们有一个遗留的 C 文件,其中包含一些用于求解线性方程的函数和几个相应的全局变量。
lineq.c:
/* macro definitions */
...
/* global vars */
...
/* some functions that make use of above variables */
void solveLinEq(...);
现在我们想在现代多线程 C++ 应用程序中使用这个遗留库。因此,我们想编写一种包装器 class LinEqSolver
,它提供用于求解线性方程的 OO 接口,并在内部调用我们遗留 C 库的函数。
但是,不同线程使用多个 LinEqSolver
实例应该是可能的。这要求每个 instance/each 线程在 lineq.c
中都有自己的全局变量副本。如果我们不想修改 lineq.c
?
,这怎么能实现
我能想到的一个可能的解决方案是将 C 文件中的全局变量和函数复制到 class LinEqSolver
中,使它们成为数据和函数成员。然后,LinEqSolver
的每个实例都将对其先前全局变量的私有副本进行操作。然而,这种复制粘贴编程风格相当糟糕,尤其是当 lineq.c
有更新并且我们需要再次将更改复制粘贴到我们的代码中时。
在求解线性方程的实际代码保留在 lineq.c
中并且仅从 LinEqSolver
中调用时,我还有哪些其他可能性?
您可以将 lineq.c
放在共享对象中,然后使用 dlopen
和 RTLD_PRIVATE
多次加载它,并为每个线程使用 dlsym
,因此全局变量是每个线程分开。不幸的是,glibc 不支持 RTLD_PRIVATE
。解决方法是为每个具有不同名称的线程复制共享对象,并使用 dlopen
到具有 RTLD_LOCAL
的共享对象副本。 lineq.c
保持不变。
我会硬着头皮修改它,将全局变量添加到一个结构中,该结构应作为指向 lineq.c 中所有调用的指针传递。
然后很容易用 C++ 包装它 class 并且每个线程都有一个实例等
另一种选择是修改 c 文件以使用 __declspec(thread) 或类似的。但是,如果您打算将此代码放入 dll 中,那么您可能会遇到无法克服的问题(至少在 Windows 上)。
您可以使用 C++11 中的 thread_local
关键字。如果您的函数 always 在开始时初始化所有需要的静态变量,这将很有效。
如果您有更复杂的图片 - 将需要更多的工作。例如:
int myVar1, myVar2;
void InitStaticVars()
{
....
}
void solveLinEq(...);
如果您只是将 thread_local
说明符添加到上述变量并在程序开头调用初始化函数,它将仅为调用线程初始化这些变量。在所有其他线程上,它们的初始值将为零。
假设我们有一个遗留的 C 文件,其中包含一些用于求解线性方程的函数和几个相应的全局变量。
lineq.c:
/* macro definitions */
...
/* global vars */
...
/* some functions that make use of above variables */
void solveLinEq(...);
现在我们想在现代多线程 C++ 应用程序中使用这个遗留库。因此,我们想编写一种包装器 class LinEqSolver
,它提供用于求解线性方程的 OO 接口,并在内部调用我们遗留 C 库的函数。
但是,不同线程使用多个 LinEqSolver
实例应该是可能的。这要求每个 instance/each 线程在 lineq.c
中都有自己的全局变量副本。如果我们不想修改 lineq.c
?
我能想到的一个可能的解决方案是将 C 文件中的全局变量和函数复制到 class LinEqSolver
中,使它们成为数据和函数成员。然后,LinEqSolver
的每个实例都将对其先前全局变量的私有副本进行操作。然而,这种复制粘贴编程风格相当糟糕,尤其是当 lineq.c
有更新并且我们需要再次将更改复制粘贴到我们的代码中时。
在求解线性方程的实际代码保留在 lineq.c
中并且仅从 LinEqSolver
中调用时,我还有哪些其他可能性?
您可以将 lineq.c
放在共享对象中,然后使用 dlopen
和 RTLD_PRIVATE
多次加载它,并为每个线程使用 dlsym
,因此全局变量是每个线程分开。不幸的是,glibc 不支持 RTLD_PRIVATE
。解决方法是为每个具有不同名称的线程复制共享对象,并使用 dlopen
到具有 RTLD_LOCAL
的共享对象副本。 lineq.c
保持不变。
我会硬着头皮修改它,将全局变量添加到一个结构中,该结构应作为指向 lineq.c 中所有调用的指针传递。
然后很容易用 C++ 包装它 class 并且每个线程都有一个实例等
另一种选择是修改 c 文件以使用 __declspec(thread) 或类似的。但是,如果您打算将此代码放入 dll 中,那么您可能会遇到无法克服的问题(至少在 Windows 上)。
您可以使用 C++11 中的 thread_local
关键字。如果您的函数 always 在开始时初始化所有需要的静态变量,这将很有效。
如果您有更复杂的图片 - 将需要更多的工作。例如:
int myVar1, myVar2;
void InitStaticVars()
{
....
}
void solveLinEq(...);
如果您只是将 thread_local
说明符添加到上述变量并在程序开头调用初始化函数,它将仅为调用线程初始化这些变量。在所有其他线程上,它们的初始值将为零。