用对象的成员函数引出线程,还是在线程中创建对象?

用对象的成员函数引出线程,还是在线程中创建对象?
还是先看一段非多线程的程序。我们用TestClass1表示在线程中创建的对象类,用TestClass2表示与创建线程的操作在同一定义域(也就是同一对{}之中的)的局部变量的对象类。
之所以提出TestClass2这种类,是因为在实际编程中我们会遇到这种情况:我们不可预知这个类何时创建以及创建多少;这个类的对象是一个新线程的参数。比如,在sokcet中的TCP server端就会有这种情况:如果每一个新连接的client都用创建一个新的线程去处理,我们不可预知在什么时候会有多少客户端连过来。
我们先观察没有多线程的时候对象的生命周期:
#include  < iostream >
#include 
" windows.h "

class  TestClass1{
private :
    
int  x;
public :
    
explicit  TestClass1( int  to_x):x(to_x)
    {}
    
~ TestClass1()
    {
        std::cerr 
<<   " destruction: 1. "   <<  std::endl;
    }
    
void  show()  const
    {
        std::cerr 
<<  x  <<  std::endl;
    }
};

class  TestClass2{
private :
    
int *  pX;
public :
    
explicit  TestClass2( int  to_x)
    {
        pX 
=   new   int ;
        
* pX  =  to_x;
    }
    
~ TestClass2()
    {
        delete pX;
        std::cerr 
<<   " destruction: 2. "    <<  std::endl;
    }
    
const   int &  value()  const
    {
        
return   * pX;
    }
};

DWORD WINAPI thread_func(LPVOID pN)
{
    Sleep(
200 );
    TestClass1 test((
* ((TestClass2 * )pN)).value());
    test.show();
    
return   0 ;
}

int  main( int  argc,  char *  argv[])
{
    
for  ( int  i  =   0 ; i  <   3 ++ i) {
        TestClass2 n(
5 );
        
        thread_func((LPVOID)
& n);
        std::cerr 
<<   " loop:  "   <<  i + 1   <<  std::endl;
    }

    Sleep(
2000 );

    std::cout 
<<   " main() ok. "   <<  std::endl;

    
return   0 ;
}
这是标准的C++模式,对象的生命周期是可以预见的:
5
destruction: 
1 .
loop: 
1
destruction: 
2 .
5
destruction: 
1 .
loop: 
2
destruction: 
2 .
5
destruction: 
1 .
loop: 
3
destruction: 
2 .
main() ok.
请按任意键继续. . .
如果我们改成线程调用:
#include  < iostream >
#include 
" windows.h "

class  TestClass1{
private :
    
int  x;
public :
    
explicit  TestClass1( int  to_x):x(to_x)
    {}
    
~ TestClass1()
    {
        std::cerr 
<<   " destruction: 1. "   <<  std::endl;
    }
    
void  show()  const
    {
        std::cerr 
<<  x  <<  std::endl;
    }
};

class  TestClass2{
private :
    
int *  pX;
public :
    
explicit  TestClass2( int  to_x)
    {
        pX 
=   new   int ;
        
* pX  =  to_x;
    }
    
~ TestClass2()
    {
        delete pX;
        std::cerr 
<<   " destruction: 2. "    <<  std::endl;
    }
    
const   int &  value()  const
    {
        
return   * pX;
    }
};

DWORD WINAPI thread_func(LPVOID pN)
{
    Sleep(
200 );
    TestClass1 test((
* ((TestClass2 * )pN)).value());
    test.show();
    
return   0 ;
}

int  main( int  argc,  char *  argv[])
{
    
for  ( int  i  =   0 ; i  <   3 ++ i) {
        TestClass2 n(
5 );
        
        HANDLE hThrd;
        DWORD thrdId;
        hThrd 
=  CreateThread(    NULL,
                                
0 ,
                                thread_func,
                                (LPVOID)
& n,
                                
0 ,
                                
& thrdId);
        
        std::cerr 
<<   " loop:  "   <<  i + 1   <<  std::endl;
    }

    Sleep(
2000 );

    std::cout 
<<   " main() ok. "   <<  std::endl;

    
return   0 ;
}
可以看到函数返回了错误的值(至于为什么每次都是36我还不清楚,但是至少不是正确的数字5),这是因为在线程调用TestClass2的对象之前已经被析构的缘故。
loop:  1
destruction: 
2 .
loop: 
2
destruction: 
2 .
loop: 
3
destruction: 
2 .
36
destruction: 
1 .
36
destruction: 
1 .
36
destruction: 
1 .
main() ok.
请按任意键继续. . .
所以,如果我们设想构造一个类,这个类的对象可以调用包含this的线程,那么这个对象一定不能是局部变量,或者说,我们必须在循环的{}对之前先把这些对象构造出来。这与我们的需求不符合——我们并不知道需要多少对象以及如何构造(比如构造TCP的通讯socket需要accept()接受客户端的信息),在这种情况下,我们只能在线程中去构造对象,这样的对象生命周期跟线程函数一样。
或者说,如果我们希望用类来封装线程,那么这些可以调用线程的对象必须是全局的。相关内容请参考本人前面的教程“初试多线程”等。

你可能感兴趣的:(用对象的成员函数引出线程,还是在线程中创建对象?)