局部变量的作用域

导航

<2010年3月>
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

统计

公告

我的邮件:

留言簿(7)

随笔分类

随笔档案

文章档案

我的链接

搜索

最新评论

阅读排行榜

评论排行榜

多线程和函数里的静态变量

试试下面这段代码的输出是什么?

#include <stdio.h>
#include <process.h>
#include <windows.h>

class foo
{
public:
    foo()
    {
        printf( "before sleep\n" );
        Sleep( 1000 );
        printf( "after sleep\n" );
    }
    void test()
    {
        printf( "in test\n" );
    }
};

foo* bar()
{
    static foo a;
    return &a;
}

unsigned __stdcall thread( void* )
{
    foo* p = bar();
    p->test();
    return 0;
}

int _cdecl main( int argc, char** argv )
{
    for( int i = 0; i < 10; ++i )
    {
        uintptr_t t = _beginthreadex( NULL, 0, thread, NULL, 0, NULL );
        CloseHandle( (HANDLE)t );
    }

    Sleep( 5000 );
    return 0;
}


不知道C/C++标准有什么规定没有, 但粗看起来好像是编译器的问题呀。我用的是vc8,谁帮忙测测别的编译器。
根据星星的建议,把输出贴出来,如下:
before sleep
in test
in test
in test
in test
in test
in test
in test
in test
in test
after sleep
in test
这里的问题是至少有10个中的9个线程没有等对象初始化完成,就已经调用对象的方法了,这肯定是不对的。我大概看了一下反汇编的结果,实际上还可能出现构造函数被调用多次的情况。
要解决这个问题,在编译器的层次上要容易一点。如果是在用户程序的层次上,则麻烦的多,因为这类方法都会涉及到另一个静态变量的初始化。

posted on 2008-05-29 09:33 局部变量 阅读(5435) 评论(19)  编辑 收藏

评论

# re: 多线程和函数里的静态变量 2008-05-29 17:46 bl


因为多线程你莫有同步。。。

class CCriticalSection 

public: 
CCriticalSection() { ::InitializeCriticalSection( &m_cs ); } 
~CCriticalSection() { ::DeleteCriticalSection( &m_cs ); } 
void Lock() { ::EnterCriticalSection( &m_cs ); } 
void Unlock() { ::LeaveCriticalSection( &m_cs ); } 
private: 
CRITICAL_SECTION m_cs; 
}; 

CCriticalSection cs;


class foo
{
public:
    foo()
    {
        printf( "before sleep\n" );
        Sleep( 3000 );
        printf( "after sleep\n" );
    }
    void test()
    {
        printf( "in test\n" );
    }
};

foo* bar()
{
cs.Lock();
    static foo a;
cs.Unlock();
    return &a;
}

unsigned __stdcall thread( void* )
{
    foo* p = bar();
    p->test();
    return 0;
}

int _cdecl main( int argc, char** argv )
{
    for( int i = 0; i < 50; ++i )
    {
        uintptr_t t = _beginthreadex( NULL, 0, thread, NULL, 0, NULL );
        CloseHandle( (HANDLE)t );
    }

    Sleep( 5000 );
    return 0;
}
 

# re: 多线程和函数里的静态变量 2008-05-29 17:49 bl


锁是放在 bar() 中好?还是 thread() 中好?

# to bl: 你的方法是错的 2008-05-29 20:01 局部变量

我在最后已经说了“
如果是在用户程序的层次上,则麻烦的多,因为这类方法都会涉及到另一个静态变量的初始化” 。所以常见的多线程同步方式在这里都会失效。
你的问题是无法保证临界区先于对象初始化,虽然也许在这个例子中确实是它先初始化的。

# re: 多线程和函数里的静态变量 2008-05-29 21:15 玻璃小屋


倒。。。....那临界区先于线程就可以了。。。

# re: 多线程和函数里的静态变量 2008-05-29 21:23 heroboy

这个例子很好的说明了,这种形式的单件的缺点就是构造的时候线程不安全

# to heroboy 2008-05-30 09:02 局部变量

握手, 这个例子正是我在考虑单件的问题时想到的, 其实单件还有几个其它问题, 打算过几天全写出来。

to 玻璃小屋:
“临界区先于线程就可以了”,在这个例子中是这样。但在实际项目中只保证这一点是不够的,必须保证临界区先于对象, 否则就可能在临界区初始化之前Enter它了。
据我所知,用户程序中要保证临界区先初始化,只能靠#pragma init_seg,但我想多数C++程序员根本不知道这个选项

# re: 多线程和函数里的静态变量 2008-05-30 10:41 bl


其实,构造函数到汇编这层后 就是一普通函数,他有可能被不同线程执行。。。 而所谓的单件 就是一全局部变量~  临界区也是全局的~ 全局的东西被构造出来之后,才能轮到 程序执行。。也就是线程执行。。。

所以说。。我认为你这样还是 对 全局部变量 进行多线程访问同步控制的问题 -.-!

# re: 多线程和静态变量 2008-06-03 11:58 清风雨

我觉得输出很正常啊,甚至输出乱码都有可能。
函数里的static 变量,在函数第一次调用时初始化,程序退出时析构(函数间的内部static变量析构顺序是不确定的)。

# re: 多线程和静态变量 2008-06-03 12:03 清风雨

不好意思,明白你的意思了。不过,我觉得编译器视乎也很难对你的static foo a;进行保护,他未必知道你要多线程使用bar函数。他要是随便多线程保护掉,那么性能上会有损失。最关键的是,这本来是用户层是否多线程事,编译器来做似乎不太合理。

# to 清风雨 2008-06-03 13:40 局部变量

对于vc来说,如果我用多线程运行时库, 它就应该保护。否则可以不保护。我想到了一种实现方法,代价根本不高.

# re: 多线程和函数里的静态变量 2008-06-04 22:15 小明

代价不高,也是有代价的。不符合C++的精神:你不需要为你不使用的东西付出代价。

这个例子,直接在线程启动前调用一下bar(),就什么问题都解决了,也不需要什么同步了

# to 小明 2008-06-05 08:39 局部变量

你的做法恰恰违反了c++的精神, 如果程序的某次运行根本不需要调用bar(),那我的做法没有为不使用的东西付出代价,而你的付出了.

# re: 多线程和函数里的静态变量 2008-06-05 12:20 boli


C++ 只是编译器的C++。。。运行期没有C++ 只有面向过程。。。

# to boli 2008-06-05 12:50 局部变量

刚才讨论的已经超出语言的范畴了:)

不过小明的另外一个问题是:不付出代价是有前提的,这个前提是“正确”,错误的做法再“廉价”也没有用

# re: 多线程和函数里的静态变量 2008-06-05 17:54 路过

靠,都説些啥啊,静态变量本身都只初始化一次,还扯出多线程的问题!郁闷!

# re: 多线程和函数里的静态变量 2008-06-06 16:08 heroboy

ls,多线程的问题会造成静态变量被初始化多次的。

# re: 多线程和函数里的静态变量 2008-11-12 13:37 GOD

请教,怎么样才能让多线程初始化多次一个静态变量?
俺试了,没成功啊.
俺还有一个问题,一个静态库中定义了静态变量,但是使用的时候发现第二次访问那个变量的时候地址根本不对啊.点解啊???

# re: 多线程和函数里的静态变量 2009-08-05 17:59 test

1.静态变量、全局变量的初始化顺序,好像是没有统一的规定,不过大多数编译器是按照这个顺序:不同的文件里的变量,按照文件的链接顺序,相同文件里的变量按照声明顺序。但是这个程序里面的问题不是静态变量初始化的问题,因为都在一个文件里面,而且先声明。补充一句销毁的顺序和初始化的顺寻相反,也就是main函数返回后调用的那个@exit函数里面的工作。
2.这个问题的原因就是在于没有为多线程做现成同步,由于静态变量只初始化一次,而其他线程没有锁,故9个线程没有等待做初始化的那个线程完成操作。直接跳到了链接时做的静态函数地址了,如果你把函数设为虚函数,估计就要出错了,因为这个时候还没有虚表。
3.至于加锁的方式,建议采用双重违例检查,配合上面bl提到的模式,就能解决这个问题。或者采用一个线程初始化所有全局、静态变量的方法。
4.c++的单例模式,如果考虑多线程的话比较复杂,有人曾经为此专门写了半本书。可以看看好像叫c++(template)设计模式

# re: 多线程和函数里的静态变量 2009-08-05 18:01 test

C++设计新思维——泛型编程与设计模式之应用

标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]