用对象的成员函数引出线程,还是在线程中创建对象?
还是先看一段非多线程的程序。我们用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++模式,对象的生命周期是可以预见的:
#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 ;
}
5
destruction: 1 .
loop: 1
destruction: 2 .
5
destruction: 1 .
loop: 2
destruction: 2 .
5
destruction: 1 .
loop: 3
destruction: 2 .
main() ok.
请按任意键继续. . .
如果我们改成线程调用:
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的对象之前已经被析构的缘故。
#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 ;
}
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()接受客户端的信息),在这种情况下,我们只能在线程中去构造对象,这样的对象生命周期跟线程函数一样。
destruction: 2 .
loop: 2
destruction: 2 .
loop: 3
destruction: 2 .
36
destruction: 1 .
36
destruction: 1 .
36
destruction: 1 .
main() ok.
请按任意键继续. . .
或者说,如果我们希望用类来封装线程,那么这些可以调用线程的对象必须是全局的。相关内容请参考本人前面的教程“初试多线程”等。