也许有什么我错过了的地方,但是...
...全局变量在线程之间共享,而不是进程...
这意味着在您的情况下,您可以有相同C程序的两个进程正在工作,并且它们不会相互干扰,除非它们与进程共享内存相关。
...如果您需要在同一进程中运行C代码的两个实例...
那么你就卡住了。
TLS,也许?
要么你可以将它们启动到单独的线程中,并将全局变量声明为线程本地存储变量。例如,在Visual C++上,以下代码:
int myGlobalVariable = 42 ; // Global variable
__declspec(thread) int myTLSVariable = 42 ; // Thread local variable
每个线程都会有自己的变量版本。这样,在线程结束时,您可以将内容复制到其他地方。
重写代码...
您不需要为此添加C++层。您可以保留您的C代码,并在结构体中声明所有全局变量:
int iMyGlobalVariable = 42 ;
const char * strMyGlobalString = NULL ;
short iMyShortData = 7 ;
typedef struct MyStruct
{
int iMyGlobalVariable ;
const char * strMyGlobalString ;
short iMyShortData ;
}
MyStruct ;
然后您修改函数的原型,使其接受指向此结构体的指针作为第一个参数,然后不再修改全局变量,而是修改结构体成员:
int foo(char *p)
{
iMyShortData = 55 ;
fooAgain("Hello World", 42) ;
}
变成了:
int foo(MyStruct * s, char *p)
{
s->iMyShortData = 55 ;
fooAgain(s, "Hello World", 42) ;
}
然后,在主函数中,你不再直接调用第一个函数,而是通过传递正确结构体的指针来调用它。不再使用:
int main(int argc, char * argv[])
{
bar(42, 55) ;
}
您编写:
int main(int argc, char * argv[])
{
MyStruct A = { } ;
MyStruct B = { } ;
bar(&A, 42, 55) ;
bar(&B, 42, 55) ;
return 0 ;
}
在上面的例子中,这两个被依次调用,但是你可以使用线程来启动它们。
如何保存全局状态?
如果你的代码是单线程的,你可以通过保存/重置全局状态来交错调用第一个实例和第二个实例。让我们使用上面相同的结构体:
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 = { } ;
MyStruct B = { } ;
resetState(&A) ;
bar(42, 55) ;
saveState(&A) ;
resetState(&B) ;
bar(42, 55) ;
saveState(&B) ;
resetState(&A) ;
foo("Hello World", 3.14159) ;
saveState(&A) ;
resetState(&B) ;
foo("Hello World", 3.14159) ;
saveState(&B) ;
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) ;
return 0 ;
}
这看起来比C版本好多了。不过,MyWrapper并不是线程安全的...
结论
第一种解决方案(TLS)是快速且简单的解决方案,而第二种则是重构代码以正确编写它(全局变量被反对有很好的理由,显然,你遇到了其中之一),第三种是一种“hack”,使您可以交错两个调用。
在这三种解决方案中,只有第二种解决方案将使其易于包装此代码在健壮的、线程安全的C++类中(如果仍然需要)。