還是先看一段非多線程的程序。我們用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()接受客戶端的信息),在這種情況下,我們只能在線程中去構造對象,這樣的對象生命周期跟線程函數一樣。
或者說,如果我們希望用類來封裝線程,那么這些可以調用線程的對象必須是全局的。相關內容請參考本人前面的教程“初試多線程”等。
posted on 2010-06-05 21:06
lf426 閱讀(796)
評論(0) 編輯 收藏 引用 所屬分類:
語言基礎、數據結構與算法 、
Win32與VC