也许有什么东西让我无法理解,但是......
...全局变量在线程之间共享,而不是在进程之间...
这意味着在您的情况下,您可以让同一个 C 程序的两个进程工作,并且它们不会干扰另一个进程,除非它们以某种方式与进程共享内存一起工作。
...如果您需要在同一进程中运行的 C 代码的两个实例...
那你就完蛋了。
TLS,也许?
您可以在单独的线程中启动它们并将全局变量声明为 Thread-Local-Storage 变量。例如,在 Visual C++ 上,以下代码:
int myGlobalVariable = 42 ; // Global variable
__declspec(thread) int myTLSVariable = 42 ; // Thread local variable
每个线程都有自己的变量版本。这样,在线程结束时,您可以将内容复制到其他地方。
重写代码...
您不需要向其中添加 C++ 层。您可以保留 C 代码,并在 struct 中声明所有全局变量:
/* C global variable */
int iMyGlobalVariable = 42 ;
const char * strMyGlobalString = NULL ;
short iMyShortData = 7 ;
/* C struct */
typedef struct MyStruct
{
int iMyGlobalVariable ;
const char * strMyGlobalString ;
short iMyShortData ;
}
MyStruct ;
然后修改函数的原型以接受指向该结构的指针作为第一个参数,然后修改结构成员而不是修改全局变量:
/* old function */
int foo(char *p)
{
/* fudge with the global variables */
iMyShortData = 55 ;
/* etc. */
fooAgain("Hello World", 42) ;
}
变成:
/* new function */
int foo(MyStruct * s, char *p)
{
/* fudge with the struct variables */
s->iMyShortData = 55 ;
/* etc. */
fooAgain(s, "Hello World", 42) ;
}
然后,在主函数中,不是调用第一个函数,而是通过给它指向正确结构的指针来调用它。代替 :
int main(int argc, char * argv[])
{
bar(42, 55) ;
}
你写 :
int main(int argc, char * argv[])
{
MyStruct A = { /* initialize A's members if needed */ } ;
MyStruct B = { /* initialize B's members if needed */ } ;
bar(&A, 42, 55) ;
bar(&B, 42, 55) ;
return 0 ;
}
在上面的示例中,两者一个接一个地被调用,但您可以启动线程。
保存全局状态?
如果您的代码是单线程的,您可以通过保存/重置全局状态来交错对第一个实例的调用和对第二个实例的调用。让我们使用上面相同的结构:
/* C global variable */
int iMyGlobalVariable = 42 ;
short iMyShortData = 7 ;
void saveState(MyStruct * s)
{
s->iMyGlobalVariable = iMyGlobalVariable ;
s->iMyShortData = iMyShortData ;
}
void resetState(const MyStruct * s)
{
iMyGlobalVariable = s->iMyGlobalVariable ;
iMyShortData = s->iMyShortData ;
}
然后,在需要时调用保存和重置函数:
int main(int argc, char * argv[])
{
MyStruct A = { /* initialize A's members if needed */ } ;
MyStruct B = { /* initialize B's members if needed */ } ;
resetState(&A) ; /* now, we work on A */
bar(42, 55) ;
saveState(&A) ; /* we save the progress on A */
resetState(&B) ; /* now, we work on B */
bar(42, 55) ;
saveState(&B) ; /* we save the progress on B */
resetState(&A) ; /* now, we work on A */
foo("Hello World", 3.14159) ;
saveState(&A) ; /* we save the progress on A */
resetState(&B) ; /* now, we work on B */
foo("Hello World", 3.14159) ;
saveState(&B) ; /* we save the progress on B */
/* etc. */
return 0 ;
}
这可以由 C++ 代码包装以自动包装 resetState/saveState 函数。例如 :
struct MyWrapper
{
void foo(const char * p, double d)
{
resetState(&m_s) ;
foo(p, d) ;
saveState(&m_s) ;
}
void bar(int i, short i2)
{
resetState(&m_s) ;
bar(i, i2) ;
saveState(&m_s) ;
}
MyStruct m_s ;
} ;
您可以将主要内容重写为:
int main(int argc, char * argv[])
{
MyWrapper A ;
MyWrapper B ;
A.bar(42, 55) ;
B.bar(42, 55) ;
A.foo("Hello World", 3.14159) ;
B.foo("Hello World", 3.14159) ;
// etc.
return 0 ;
}
这看起来比 C 版本好多了。尽管如此, MyWrapper 不是线程安全的......
结论
第一个解决方案 (TLS) 是快速的'n'dirty 解决方案,而第二个是重构代码以正确编写它(有很好的理由不赞成全局变量,显然,你偶然发现了其中一个),并且第三个是“hack”,使您可以将两个呼叫交错。
在所有三个解决方案中,如果仍然需要,只有第二个可以轻松将此代码包装在健壮的、线程安全的 C++ 类中。