線程中的變量
由于每個線程都代表了一個不同的執行路徑,因此,最好有一種只限于一個線程內部使用的數據,
要實現上述目的有以下幾種方式:
1、局部變量(基于棧),很簡單,在你的線程函數中你定義的變量既是如此。由于每個線程都在各自的棧中,各個線程將都有一套局部變量的副本,這樣,就不會相互影響。對于那些只在過程或函數的生存期有意義的變量,應當把它們聲明爲局部變量。
2、存儲在線程對象中。還記得CreateThread函數中的lpParameter參數嗎,它可以接受一個無類型的指針。結合本文第一章的內容,你應該還記得,它被存儲在線程內核對象的上下文結構中,你可以通過CONTEXT結構中的CONTEXT_INTEGER部分的ebx來讀取它的地址。
下面是一段示例代碼,用來演示讀取CONTEXT結構,這段代碼一般用不到,但它可以說明CrateThread函數中的lpParameter被存儲的位置
...{
作者:wudi_1982
聯系方式:wudi_1982@hotmail.com
轉載請著名出處,本代碼爲演示代碼,只貼出了一些關鍵部分
}
type
//傳遞給線程函數的結構和指針的聲明
Tinfo = record
count : integer;
x : integer;
y : integer;
end;
Pinfo= ^Tinfo;
var
MyThreadHad : THandle;//一個全局變量,用來保存線程的句柄
//線程函數
function MyThread(info : Pointer):DWORD; stdcall;
var
i : integer;
begin
//根據傳遞來信息決定在窗口的那個位置輸出什麽信息
for i := 0 to Pinfo(info)^.count-1 do
Form1.Image1.Canvas.TextOut(Pinfo(info)^.x,Pinfo(info)^.y,inttostr(i));
//FreeMem(info);
Result := 0;
end;
//創建一個線程
procedure TForm1.Button4Click(Sender: TObject);
var
ppi : Pinfo;
MyThreadId : DWORD;
begin
//分配空間並賦初值
ppi :=AllocMem(sizeof(tinfo));
ppi^.count := 1000000;
ppi^.x := 10;
ppi^.y := 10;
//創建
MyThreadHad := CreateThread(nil,0,@MyThread,ppi,CREATE_SUSPENDED,MyThreadId);
//在窗體上顯示線程函數的地址和傳遞給它的參數的地址
labThreadAddr.Caption := inttostr( integer(@MyThread));
labThreadPvparam.Caption := inttostr(integer(ppi));
end;
//讀取CONTEXT結構,注意CONTEXT結構是和CPU有關的,我這裏測試時,工作在intel的CPU上
procedure TForm1.btnRContextClick(Sender: TObject);
var
con : _CONTEXT;
begin
//初始化結構
con.ContextFlags := CONTEXT_FULL;
//讀取
GetThreadContext(MyThreadHad,con);
//顯示在窗體的listbox上
with lbxContextInfo.Items do
begin
// Clear;
Add('------------CONTEXT--------------');
Add('');
Add('CONTEXT_DEBUG_REGISTERS-----');
Add('dr0:'+#9+IntToStr(con.Dr0));
Add('dr1:'+#9+IntToStr(con.Dr1));
Add('dr2:'+#9+IntToStr(con.Dr2));
Add('dr3:'+#9+IntToStr(con.Dr3));
Add('dr6:'+#9+IntToStr(con.Dr6));
Add('dr7:'+#9+IntToStr(con.Dr7));
add('CONTEXT_SEGMENTS---------');
Add('segGs:'+#9+inttostr(con.SegGs));
Add('segFs:'+#9+inttostr(con.Segfs));
Add('segEs:'+#9+inttostr(con.Seges));
Add('segDs:'+#9+inttostr(con.Segds));
Add('CONTEXT_INTEGER.---------');
Add('edi: '+#9+IntToStr(con.Edi));
Add('esi: '+#9+IntToStr(con.Esi));
Add('ebx: '+#9+IntToStr(con.Ebx));
Add('edx: '+#9+IntToStr(con.Edx));
Add('ecx: '+#9+IntToStr(con.Ecx));
Add('eax: '+#9+IntToStr(con.Eax));
Add('CONTEXT_CONTROL----------');
Add('Ebp: '+#9+IntToStr(con.Ebp));
Add('Eip: '+#9+IntToStr(con.Eip));
Add('segcs: '+#9+IntToStr(con.SegCs));
Add('EFlags: '+#9+IntToStr(con.EFlags));
Add('Esp: '+#9+IntToStr(con.Esp));
Add('SegSs: '+#9+IntToStr(con.SegSs));
end;
end;
把上面代碼整理之後,添加到你的程序中,你可以發現(如果也是intel的CPU),那麽你可以從Eax寄存器讀取到線程函數的地址,從Ebx中讀取到傳遞給線程函數的參數地址。
在DELPHI中的TThread對象的構造函數中,你可以看到這段代碼
FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
再觀察BeginThread的實現,你會發現TThread的調用CreateThread時,將Pointer(Self),也就是TThread對象本身當作線程函數的參數傳遞過去,換言之,你在TThread的派生類中定義的變量,對于一個線程而言,將存儲在這個線程單獨的堆棧中,而它在堆棧的地址存儲在線程的上下文結構中。
可以做一個簡單的試驗,將一個線程生成多次,你可以發現存儲在線程對象內部的變量將互不影響。
說到這裏,必須談論一個問題,效率的問題,我在一本書上曾經看到過這樣一段話“由于訪問線程對象中的數據比訪問線程局部變量要快10倍,因此,你應當盡可能地把線程專用的信息保存在線程對象中。”對此,我一直沒有特別理解。如果一定要相信這句話,那我會這麽理解,就是存儲在線程對象中的變量因爲上下文結構記錄了它的地址等原因,所以它更快。盡信書不如無書,我還在思考,不過好在這種速度的影響對于通常的使用而言影響不大。
3、在DELPHI中,用Object Pascal的關鍵字threadvar來聲明變量,以利用操作系統級的線程局部存儲。
在前面我們了解到:雖然對于局部變量,在每個線程中都一個副本,然而應用程序的全局變量是被所有線程所共享的。當多個線程對這個全局變量進行訪問時,將可能出現很多未知的問題,Win32提供了一種稱爲線程局部存儲的方式,它能使你在第一個運行的線程中創建一個全局變量的拷貝。Delphi利用關鍵字threadvar封裝此功能。在threadvar關鍵字下你可以聲明任何局部存儲的變量。
4、全局變量,多線程最讓人頭疼的地方就是全局變量了,好的同步方式將決定你高效、安全的訪問全局變量,雖然上述的threadvar是解決全局變量線程局部存儲的一個辦法,但在我實際的編碼工作中,幾乎很少用它,它的局限性太多。多線程訪問全局變量的方法將在下一文中詳細描述。
注:轉載請著名出處,謝謝!
WIN32下DELPHI中的多線程【線程的調度】(二)線程的調度 每個線程是擁有一個上下文結構的,這個結構維護在線程的內核對象中。這個上下文結構反映了線程上次運行時該線程的C P U寄存器的狀態。每隔20ms左右,Windows要查看當前存在的所有線程內核對象。在...查看完整版>>
WIN32下DELPHI中的多線程【線程的調度】(二)
WIN32下DELPHI中的多線程【同步1】(四)線程的同步 在使用的時候,多線程最讓人頭疼的也許就是同步了。 如果你的線程只是完成一件並不需要訪問線程對象外部資源的工作,在這種情況下,線程互相之間不需要進行通信,此時Windows的運行性能最好。但是...查看完整版>>
WIN32下DELPHI中的多線程【同步1】(四)
WIN32下DELPHI中的多線程【同步2】(五)線程同步2 上一文中曾經介紹了線程同步的一些方法,其實完成同步還有很多很多的辦法,這裏最後介紹一種方式--信號量內核對象。並借此來回顧線程同步。 在談論信號量之前,我想先談論另外一種方式,一種你...查看完整版>>
WIN32下DELPHI中的多線程【同步2】(五)
存儲過程中的top+變量(downmoon)存儲過程中的top+變量(downmoon) 存儲過程中的top+變量(downmoon) .lineBorderBlue1 { BORDER-TOP: #999999 1px groove; BORDER-BOTTOM: #999999 1px groove; BORDER-LEFT: #999999 1px groove; BORDER-...查看完整版>>
存儲過程中的top+變量(downmoon)
存儲過程中的top+變量存儲過程中的TOP後跟一個變量會如何? Create proc getWorkPlan2@intCounter int,@lngUserID intas select Top 5 lngWorkID,strWorkName,strExecHumanName,strBeginDate from worklist where lngExecHumanID= @lngUs...查看完整版>>
存儲過程中的top+變量