将遗留 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 放在共享对象中,然后使用 dlopenRTLD_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 说明符添加到上述变量并在程序开头调用初始化函数,它将仅为调用线程初始化这些变量。在所有其他线程上,它们的初始值将为零。