本来是想重现以前工作中遇到的一个Segment Fault。但是据说原来的问题已经在新的C++版本中解决了,现在不会产生段错误。而且C++的实现版本众多,也无从考究原来的版本是哪一个了。

原来的代码模型如下,在多线程环境中,重复进入这个函数,可能会有多个构造函数同时运行。从而会产生段错误。当然这个概率比较小,好像在性能比较差的机器上较容易重现。

void func()
{
    static string crash("It is none of my business!");
}

但这类问题现在仍然有效。可以参考《Effective C++(第三版)》中的条款4:确定对象被使用前已先被初始化。其中讲到,任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。

参考以下代码:

#include <iostream>
#include <string>
#include <windows.h>

using namespace std;        

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    static string x(10000000, 'e');

    if (x.length() == 0) {
        cout << "Oh!" << endl;
    }
    else {
        cout << x.length() << endl;
    }

    return 0;
}

int main()
{
    CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    Sleep(100);
    return 0;
}

在我的Visual Studio 2013 Express中的运行结果为:

Oh!
10000000
请按任意键继续. . .

为了稳定重现这个问题,这里使用了string(num,char)这个构造函数,好让子弹飞得久一点。从结果看,确实有麻烦,当第二个线程访问字符串的时候,第一个线程还没有把那个静态字符串初始化完成。所以不管是否利用了什么Copy-On-Write或引用计数的技术与否,此时字符串的长度为0,使用的时候可能会出现问题。