日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

delphi 中几种多线程操作方式

發布時間:2023/12/10 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 delphi 中几种多线程操作方式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在了解多線程之前我們先了解一下進程和線程的關系

一個程序至少有一個主進程,一個進程至少有一個線程。

主線程又程為UI線程。

進程和線程的主要差別在于它們是不同的操作系統資源管理方式。進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對于一些要求同時進行并且又要共享某些變量的并發操作,只能用線程,不能用進程。如果有興趣深入的話,我建議你們看看《現代操作系統》或者《操作系統的設計與實現》。對就個問題說得比較清楚。

多線程應該是編程工作者的基礎技能, 但這個基礎我從來沒學過,所以僅僅是看上去會一些,明白了2+2的時候,其實我還不知道1+1。

開始本應該是一篇洋洋灑灑的文字, 不過我還是提倡先做起來, 在嘗試中去理解.


先試試這個:

procedure?TForm1.Button1Click(Sender:?TObject);? var?i:?Integer;? begin?for?i?:=?0?to?500000?do? ??begin? ????Canvas.TextOut(10,?10,?IntToStr(i));? ??end;? end;?
上面程序運行時, 我們的窗體基本是 "死" 的, 可以在你在程序運行期間拖動窗體試試...

Delphi 為我們提供了一個簡單的辦法(Application.ProcessMessages)來解決這個問題:
procedure?TForm1.Button1Click(Sender:?TObject);? var?i:?Integer;? begin?for?i?:=?0?to?500000?do? ??begin? ????Canvas.TextOut(10,?10,?IntToStr(i));? ????Application.ProcessMessages;? ??end;? end;?
這個 Application.ProcessMessages; 一般用在比較費時的循環中, 它會檢查并先處理消息隊列中的其他消息.

但這算不上多線程, 譬如: 運行中你拖動窗體, 循環會暫停下來...

在使用多線程以前, 讓我們先簡單修改一下程序:
function?MyFun:?Integer;? var?i:?Integer;? begin?for?i?:=?0?to?500000?do? ??begin? ????Form1.Canvas.Lock;? ????Form1.Canvas.TextOut(10,?10,?IntToStr(i));? ????Form1.Canvas.Unlock;? ??end;? ??Result?:=?0;? end;? ? procedure?TForm1.Button1Click(Sender:?TObject);? begin? ??MyFun;? end;?
細數上面程序的變化:
1、首先這還不是多線程的, 也會讓窗體假 "死" 一會;
2、把執行代碼寫在了一個函數里, 但這個函數不屬于 TForm1 的方法, 所以使用 Canvas 是必須冠以名稱(Form1);
3、既然是個函數, (不管是否必要)都應該有返回值;
4、使用了 500001 次 Lock 和 Unlock.

Canvas.Lock 好比在說: Canvas(繪圖表面)正忙著呢, 其他想用 Canvas 的等會;
Canvas.Unlock : 用完了, 解鎖!

在 Canvas 中使用 Lock 和 Unlock 是個好習慣, 在不使用多線程的情況下這無所謂, 但保不準哪天程序會擴展為多線程的; 我們現在學習多線程, 當然應該用.

在 Delphi 中使用多線程有兩種方法: 調用 API、使用 TThread 類; 使用 API 的代碼更簡單.
function?MyFun(p:?Pointer):?Integer;?stdcall;? var?i:?Integer;? begin?for?i?:=?0?to?500000?do? ??begin? ????Form1.Canvas.Lock;? ????Form1.Canvas.TextOut(10,?10,?IntToStr(i));? ????Form1.Canvas.Unlock;? ??end;? ??Result?:=?0;? end;? ? procedure?TForm1.Button1Click(Sender:?TObject);? var? ??ID:?THandle;? begin? ??CreateThread(nil,?0,?@MyFun,?nil,?0,?ID);? end;?
代碼分析:
CreateThread 一個線程后, 算上原來的主線程, 這樣程序就有兩個線程、是標準的多線程了;
CreateThread 第三個參數是函數指針, 新線程建立后將立即執行該函數, 函數執行完畢, 系統將銷毀此線程從而結束多線程的故事.

CreateThread 要使用的函數是系統級別的, 不能是某個類(譬如: TForm1)的方法, 并且有嚴格的格式(參數、返回值)要求, 不管你暫時是不是需要都必須按格式來;
因為是系統級調用, 還要綴上 stdcall, stdcall 是協調參數順序的, 雖然這里只有一個參數沒有順序可言, 但這是使用系統函數的慣例.

CreateThread 還需要一個 var 參數來接受新建線程的 ID, 盡管暫時沒用, 但這也是格式; 其他參數以后再說吧.

這樣一個最簡單的多線程程序就出來了, 咱們再用 TThread 類實現一次
type?TMyThread?=?class(TThread)?protected?procedure?Execute;?override;?end;? ? procedure?TMyThread.Execute;? var? ??i:?Integer;? begin? ??FreeOnTerminate?:=?True;?{這可以讓線程執行完畢后隨即釋放}? ??for?i?:=?0?to?500000?do? ??begin? ????Form1.Canvas.Lock;? ????Form1.Canvas.TextOut(10,?10,?IntToStr(i));? ????Form1.Canvas.Unlock;? ??end;? end;? ? procedure?TForm1.Button1Click(Sender:?TObject);? begin? ??TMyThread.Create(False);? end;? ? TThread 類有一個抽象方法(Execute), 因而是個抽象類, 抽象類只能繼承使用, 上面是繼承為 TMyThread.

繼承 TThread 主要就是實現抽象方法 Execute(把我們的代碼寫在里面), 等我們的 TMyThread 實例化后, 首先就會執行 Execute 方法中的代碼.

按常規我們一般這樣去實例化:
procedure?TForm1.Button1Click(Sender:?TObject);? var?MyThread:?TMyThread;? begin?MyThread?:=?TMyThread.Create(False);? end;? 因為 MyThread 變量在這里毫無用處(并且編譯器還有提示), 所以不如直接寫做 TMyThread.Create(False);

我們還可以輕松解決一個問題, 如果: TMyThread.Create(True) ?
這樣線程建立后就不會立即調用 Execute, 可以在需要的時候再用 Resume 方法執行線程, 譬如:
procedure?TForm1.Button1Click(Sender:?TObject);? var?MyThread:?TMyThread;? begin?MyThread?:=?TMyThread.Create(True);?MyThread.Resume;? end;?//可簡化為:? procedure?TForm1.Button1Click(Sender:?TObject);? begin? ??with?TMyThread.Create(True)?do?Resume;? end;?
一、入門
㈠、
function?CreateThread(?lpThreadAttributes:?Pointer;???????????{安全設置}?dwStackSize:?DWORD;????????????????????{堆棧大小}?lpStartAddress:?TFNThreadStartRoutine;?{入口函數}?lpParameter:?Pointer;??????????????????{函數參數}? ??dwCreationFlags:?DWORD;????????????????{啟動選項}? ??var?lpThreadId:?DWORD??????????????????{輸出線程?ID?}? ):?THandle;?stdcall;?????????????????????{返回線程句柄}?
在 Windows 上建立一個線程, 離不開 CreateThread 函數;
TThread.Create 就是先調用了 BeginThread (Delphi 自定義的), BeginThread 又調用的 CreateThread.
既然有建立, 就該有釋放, CreateThread 對應的釋放函數是: ExitThread, 譬如下面代碼:
procedure?TForm1.Button1Click(Sender:?TObject);? begin?ExitThread(0);?{此句即可退出當前程序,?但不建議這樣使用}? end;?
代碼注釋:
當前程序是一個進程, 進程只是一個工作環境, 線程是工作者;
每個進程都會有一個啟動線程(或叫主線程), 也就是說: 我們之前大量的編碼都是寫給這個主線程的;
上面的 ExitThread(0); 就是退出這個主線程;
系統不允許一個沒有線程的進程存在, 所以程序就退出了.
另外: ExitThread 函數的參數是一個退出碼, 這個退出碼是給之后的其他函數用的, 這里隨便給個無符號整數即可.

或許你會說: 這個 ExitThread 挺好用的; 其實不管是用 API 還是用 TThread 類寫多線程, 我們很少用到它; 因為:
1、假如直接使用 API 的 CreateThread, 它執行完入口函數后會自動退出, 無需 ExitThread;
2、用 TThread 類建立的線程又絕不能使用 ExitThread 退出; 因為使用 TThread 建立線程時會同時分配更多資源(譬如你自定義的成員、還有它的祖先類(TObject)分配的資源等等), 如果用 ExitThread 給草草退出了, 這些資源將得不到釋放而導致內存泄露. 盡管 Delphi 提供了 EndThread(其內部調用 ExitThread), 這也不需要我們手動操作(假如非要手動操作也是件很麻煩的事情, 因為很多時候你不知道線程是什么時候執行完畢的).
除了 CreateThread, 還有一個 CreateRemoteThread, 可在其他進程中建立線程, 這不應該是現在學習的重點;
現在先集中精力把 CreateThread 的參數搞徹底.

倒著來吧, 先談談 CreateThread 將要返回的 "線程句柄".

"句柄" 類似指針, 但通過指針可讀寫對象, 通過句柄只是使用對象;
有句柄的對象一般都是系統級別的對象(或叫內核對象); 之所以給我們的是句柄而不是指針, 目的只有一個: "安全";
貌似通過句柄能做很多事情, 但一般把句柄提交到某個函數(一般是系統函數)后, 我們也就到此為止很難了解更多了; 事實上是系統并不相信我們.

不管是指針還是句柄, 都不過是內存中的一小塊數據(一般用結構描述), 微軟并沒有公開句柄的結構細節, 猜一下它應該包括: 真實的指針地址、訪問權限設置、引用計數等等.

既然 CreateThread 可以返回一個句柄, 說明線程屬于 "內核對象".
實際上不管線程屬于哪個進程, 它們在系統的懷抱中是平等的; 在優先級(后面詳談)相同的情況下, 系統會在相同的時間間隔內來運行一下每個線程, 不過這個間隔很小很小, 以至于讓我們誤以為程序是在不間斷地運行.

這時你應該有一個疑問: 系統在去執行其他線程的時候, 是怎么記住前一個線程的數據狀態的?
有這樣一個結構 TContext, 它基本上是一個 CPU 寄存器的集合, 線程是數據就是通過這個結構切換的, 我們也可以通過 GetThreadContext 函數讀取寄存器看看.

附上這個結構 TContext(或叫: CONTEXT、_CONTEXT) 的定義:
PContext?=?^TContext;? _CONTEXT?=?record?ContextFlags:?DWORD;?Dr0:?DWORD;?Dr1:?DWORD;?Dr2:?DWORD;?Dr3:?DWORD;?Dr6:?DWORD;?Dr7:?DWORD;?FloatSave:?TFloatingSaveArea;?SegGs:?DWORD;?SegFs:?DWORD;?SegEs:?DWORD;?SegDs:?DWORD;?Edi:?DWORD;?Esi:?DWORD;?Ebx:?DWORD;?Edx:?DWORD;?Ecx:?DWORD;?Eax:?DWORD;?Ebp:?DWORD;?Eip:?DWORD;?SegCs:?DWORD;?EFlags:?DWORD;?Esp:?DWORD;?SegSs:?DWORD;? end;?
CreateThread 的最后一個參數是 "線程的 ID";
既然可以返回句柄, 為什么還要輸出這個 ID? 現在我知道的是:
1、線程的 ID 是唯一的; 而句柄可能不只一個, 譬如可以用 GetCurrentThread 獲取一個偽句柄、可以用 DuplicateHandle 復制一個句柄等等.
2、ID 比句柄更輕便.

在主線程中 GetCurrentThreadId、MainThreadID、MainInstance 獲取的都是主線程的 ID.
㈡、啟動選項
function?CreateThread(?lpThreadAttributes:?Pointer;?dwStackSize:?DWORD;?lpStartAddress:?TFNThreadStartRoutine;?lpParameter:?Pointer;?dwCreationFlags:?DWORD;?{啟動選項}?var?lpThreadId:?DWORD? ):?THandle;?stdcall; CreateThread 的倒數第二個參數 dwCreationFlags(啟動選項) 有兩個可選值:
0: 線程建立后立即執行入口函數;
CREATE_SUSPENDED: 線程建立后會掛起等待.

可用 ResumeThread 函數是恢復線程的運行; 可用 SuspendThread 再次掛起線程.
這兩個函數的參數都是線程句柄, 返回值是執行前的掛起計數.

什么是掛起計數?
SuspendThread 會給這個數 +1; ResumeThread 會給這個數 -1; 但這個數最小是 0.
當這個數 = 0 時, 線程會運行; > 0 時會掛起.
如果被 SuspendThread 多次, 同樣需要 ResumeThread 多次才能恢復線程的運行.

在下面的例子中, 有新線程不斷給一個全局變量賦隨機值;
同時窗體上的 Timer 控件每隔 1/10 秒就把這個變量寫在窗體標題;
在這個過程中演示了 ResumeThread、SuspendThread 兩個函數.

//上面圖片中演示的代碼。? unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs,?StdCtrls,?ExtCtrls;?type? ??TForm1?=?class(TForm)? ????Button1:?TButton;? ????Button2:?TButton;? ????Button3:?TButton;? ????Timer1:?TTimer;? ????procedure?Button1Click(Sender:?TObject);? ????procedure?Button2Click(Sender:?TObject);? ????procedure?Button3Click(Sender:?TObject);? ????procedure?FormCreate(Sender:?TObject);? ????procedure?Timer1Timer(Sender:?TObject);? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? var? ??hThread:?THandle;?{線程句柄}? ??num:?Integer;?????{全局變量,?用于記錄隨機數}? ? {線程入口函數}? function?MyThreadFun(p:?Pointer):?Integer;?stdcall;? begin? ??while?True?do?{假如線程不掛起,?這個循環將一直循環下去}? ??begin? ????num?:=?Random(100);? ??end;? ??Result?:=?0;? end;? ? {建立并掛起線程}? procedure?TForm1.Button1Click(Sender:?TObject);? var? ??ID:?DWORD;? begin? ??hThread?:=?CreateThread(nil,?0,?@MyThreadFun,?nil,?CREATE_SUSPENDED,?ID);? ??Button1.Enabled?:=?False;? end;? ? {喚醒并繼續線程}? procedure?TForm1.Button2Click(Sender:?TObject);? begin? ??ResumeThread(hThread);? end;? ? {掛起線程}? procedure?TForm1.Button3Click(Sender:?TObject);? begin? ??SuspendThread(hThread);? end;? ? procedure?TForm1.FormCreate(Sender:?TObject);? begin? ??Timer1.Interval?:=?100;? end;? ? procedure?TForm1.Timer1Timer(Sender:?TObject);? begin? ??Text?:=?IntToStr(num);? end;? ? end. ㈢、入口函數的參數
function?CreateThread(?lpThreadAttributes:?Pointer;?dwStackSize:?DWORD;?lpStartAddress:?TFNThreadStartRoutine;?lpParameter:?Pointer;??{入口函數的參數}?dwCreationFlags:?DWORD;?var?lpThreadId:?DWORD? ):?THandle;?stdcall; 線程入口函數的參數是個無類型指針(Pointer), 用它可以指定任何數據; 本例是把鼠標點擊窗體的坐標傳遞給線程的入口函數, 每次點擊窗體都會創建一個線程.

運行效果圖:

//上面演示的代碼? unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs;?type? ??TForm1?=?class(TForm)? ????procedure?FormMouseUp(Sender:?TObject;?Button:?TMouseButton;? ??????Shift:?TShiftState;?X,?Y:?Integer);? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? var? ??pt:?TPoint;?{這個坐標點將會已指針的方式傳遞給線程,?它應該是全局的}? ? function?MyThreadFun(p:?Pointer):?Integer;?stdcall;? var? ??i:?Integer;? ??pt2:?TPoint;???????{因為指針參數給的點隨時都在變,?需用線程的局部變量存起來}? begin? ??pt2?:=?PPoint(p)^;?{轉換}? ??for?i?:=?0?to?1000000?do? ??begin? ????with?Form1.Canvas?do?begin? ??????Lock;? ??????TextOut(pt2.X,?pt2.Y,?IntToStr(i));? ??????Unlock;? ????end;? ??end;? ??Result?:=?0;? end;? ? procedure?TForm1.FormMouseUp(Sender:?TObject;?Button:?TMouseButton;? ??Shift:?TShiftState;?X,?Y:?Integer);? var? ??ID:?DWORD;? begin? ??pt?:=?Point(X,?Y);? ??CreateThread(nil,?0,?@MyThreadFun,?@pt,?0,?ID);? ??{下面這種寫法更好理解,?其實不必,?因為?PPoint?會自動轉換為?Pointer?的}? ??//CreateThread(nil,?0,?@MyThreadFun,?Pointer(@pt),?0,?ID);? end;? ? end.
這個例子還有不嚴謹的地方: 當一個線程 Lock 窗體的 Canvas 時, 其他線程在等待; 線程在等待時, 其中的計數也還在增加. 這也就是說: 現在并沒有去處理線程的同步; 同步是多線程中最重要的課題, 快到了.

另外有個小技巧: 線程函數的參數是個 32 位(4個字節)的指針, 僅就本例來講, 可以讓它的 "高16位" 和 "低16位" 分別攜帶 X 和 Y; 這樣就不需要哪個全局的 pt 變量了.
其實在 Windows 的消息中就是這樣傳遞坐標的, 在 Windows 的消息中一般高字節是 Y、低字節是 X; 咱們這么來吧, 這樣還可以使用給消息準備的一些方便的函數.

重寫本例代碼(當然運行效果和窗體文件都是一樣的):
unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs;?type?TForm1?=?class(TForm)? ????procedure?FormMouseUp(Sender:?TObject;?Button:?TMouseButton;? ??????Shift:?TShiftState;?X,?Y:?Integer);? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? function?MyThreadFun(p:?Pointer):?Integer;?stdcall;? var? ??i:?Integer;? ??x,y:?Word;? begin? ??x?:=?LoWord(Integer(p));? ??y?:=?HiWord(Integer(p));? ??{如果不使用?LoWord、HiWord?函數可以像下面這樣:?}? ??//x?:=?Integer(p);? ??//y?:=?Integer(p)?shr?16;? ??for?i?:=?0?to?1000000?do? ??begin? ????with?Form1.Canvas?do?begin? ??????Lock;? ??????TextOut(x,?y,?IntToStr(i));? ??????Unlock;? ????end;? ??end;? ??Result?:=?0;? end;? ? procedure?TForm1.FormMouseUp(Sender:?TObject;?Button:?TMouseButton;? ??Shift:?TShiftState;?X,?Y:?Integer);? var? ??ID:?DWORD;? ??num:?Integer;? begin? ??num?:=?MakeLong(X,?Y);? ??{如果不使用?MekeLong、MakeWParam、MakeLParam、MakeResult?等函數,?可以像下面這樣:?}? ??//num?:=?Y?shl?16?+?X;? ??CreateThread(nil,?0,?@MyThreadFun,?Ptr(num),?0,?ID);? ??{上面的?Ptr?是專門將一個數字轉換為指針的函數,?當然也可以這樣:?}? ??//CreateThread(nil,?0,?@MyThreadFun,?Pointer(num),?0,?ID);? end;? ? end. ㈣、入口函數的指針
function?CreateThread(?lpThreadAttributes:?Pointer;?dwStackSize:?DWORD;?lpStartAddress:?TFNThreadStartRoutine;?{入口函數的指針}?lpParameter:?Pointer;??dwCreationFlags:?DWORD;?var?lpThreadId:?DWORD? ):?THandle;?stdcall;

到了入口函數了, 學到這個地方, 我查了一個入口函數的標準定義, 這個函數的標準返回值應該是 DWORD, 不過這函數在 Delphi 的 System 單元定義的是: TThreadFunc = function(Parameter: Pointer): Integer; 我以后會盡量使用 DWORD 做入口函數的返回值.

這個返回值有什么用呢?
等線程退出后, 我們用 GetExitCodeThread 函數獲取的退出碼就是這個返回值!

如果線程沒有退出, GetExitCodeThread 獲取的退出碼將是一個常量 STILL_ACTIVE (259); 這樣我們就可以通過退出碼來判斷線程是否已退出.

還有一個問題: 前面也提到過, 線程函數不能是某個類的方法! 假如我們非要線程去執行類中的一個方法能否實現呢?
盡管可以用 Addr(類名.方法名) 或 MethodAddress('published 區的方法名') 獲取類中方法的地址, 但都不能當做線程的入口函數, 原因可能是因為類中的方法的地址是在實例化為對象時動態分配的.
后來換了個思路, 其實很簡單: 在線程函數中再調用方法不就得了, 估計 TThread 也應該是這樣.

下面的例子就嘗試了用線程調用 TForm1 類中的方法, 并測試了退出碼的相關問題.

unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs,?StdCtrls;?type?TForm1?=?class(TForm)? ????Button1:?TButton;? ????Button2:?TButton;? ????procedure?Button1Click(Sender:?TObject);? ????procedure?Button2Click(Sender:?TObject);? ????private? ??????procedure?FormProc;?{準備給線程使用的方法}? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? var? ??hThread:?THandle;? ? {線程入口函數}? function?MyThreadFun(p:?Pointer):?DWORD;?stdcall;? begin? ??Form1.FormProc;?{調用?TForm1?類的方法}? ??Result?:=?99;???{這個返回值將成為線程的退出代碼,?99?是我隨意給的數字}? end;? ? {TForm1?的方法,?本例中是給線程的入口函數調用的}? procedure?TForm1.FormProc;? var? ??i:?Integer;? begin? ??for?i?:=?0?to?200000?do? ??begin? ????with?Form1.Canvas?do?begin? ??????Lock;? ??????TextOut(10,?10,?IntToStr(i));? ??????Unlock;? ????end;? ??end;? end;? ? {建立并執行線程}? procedure?TForm1.Button1Click(Sender:?TObject);? var? ??ID:?DWORD;? begin? ??hThread?:=?CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? end;? ? {獲取線程的退出代碼,?并判斷線程是否退出}? procedure?TForm1.Button2Click(Sender:?TObject);? var? ??ExitCode:?DWORD;? begin? ??GetExitCodeThread(hThread,?ExitCode);? ? ??if?hThread?=?0?then? ??begin? ????Text?:=?'線程還未啟動';? ????Exit;? ??end;? ? ??if?ExitCode?=?STILL_ACTIVE?then? ????Text?:=?Format('線程退出代碼是:?%d,?表示線程還未退出',?[ExitCode])? ??else? ????Text?:=?Format('線程已退出,?退出代碼是:?%d',?[ExitCode]);? end;? ? end. ㈤、堆棧大小
function?CreateThread(?lpThreadAttributes:?Pointer;?dwStackSize:?DWORD;??{堆棧大小}?lpStartAddress:?TFNThreadStartRoutine;??lpParameter:?Pointer;??dwCreationFlags:?DWORD;?var?lpThreadId:?DWORD? ):?THandle;?stdcall;

CreateThread 的第二個參數是分配給線程的堆棧大小.
這首先這可以讓我們知道: 每個線程都有自己獨立的堆棧(也擁有自己的消息隊列).

什么是堆棧? 其實堆是堆、棧是棧, 有時 "棧" 也被叫做 "堆棧".
它們都是進程中的內存區域, 主要是存取方式不同(棧:先進后出; 堆:先進先出);
"棧"(或叫堆棧)適合存取臨時而輕便的變量, 主要用來儲存局部變量; 譬如 for i := 0 to 99 do 中的 i 就只能存于棧中, 你把一個全局的變量用于 for 循環計數是不可以的.

現在我們知道了線程有自己的 "棧", 并且在建立線程時可以分配棧的大小.

前面所有的例子中, 這個值都是 0, 這表示使用系統默認的大小, 默認和主線程棧的大小一樣, 如果不夠用會自動增長;
那主線程的棧有多大? 這個值是可以設定的: Project -> Options -> linker -> memory size(如圖)

棧是私有的但堆是公用的, 如果不同的線程都來使用一個全局變量有點亂套;
為解決這個問題 Delphi 為我們提供了一個類似 var 的 ThreadVar 關鍵字, 線程在使用 ThreadVar 聲明的全局變量時會在各自的棧中留一個副本, 這樣就解決了沖突. 不過還是盡量使用局部變量, 或者在繼承 TThread 時使用類的成員變量, 因為 ThreadVar 的效率不好, 據說比局部變量能慢 10 倍.

在下面的例子就測試了用 var 和 ThreadVar 定義變量的不同.
使用 var 效果圖:

使用 ThreadVar 效果圖:

unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs,?StdCtrls;?type?TForm1?=?class(TForm)? ????Button1:?TButton;? ????procedure?Button1Click(Sender:?TObject);? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? //var?num:?Integer;?????{全局變量}? threadvar?num:?Integer;?{支持多線程的全局變量}? ? function?MyThreadFun(p:?Pointer):?DWORD;?stdcall;? var? ??py:?Integer;? begin? ??py?:=?Integer(p);? ??while?True?do? ??begin? ????Inc(num);? ????with?Form1.Canvas?do?begin? ??????Lock;? ??????TextOut(20,?py,?IntToStr(num));? ??????Unlock;? ????end;? ????Sleep(1000);?{然線程掛起?1?秒鐘再繼續}? ??end;? end;? ? procedure?TForm1.Button1Click(Sender:?TObject);? var? ??ID:?DWORD;? begin? ??{借入口函數的參數傳遞了一個坐標點中的?Y?值,?以讓各線程把結果輸出在不同位置}? ??CreateThread(nil,?0,?@MyThreadFun,?Ptr(20),?0,?ID);? ??CreateThread(nil,?0,?@MyThreadFun,?Ptr(40),?0,?ID);? ??CreateThread(nil,?0,?@MyThreadFun,?Ptr(60),?0,?ID);? end;? ? end. ㈥、安全設置
function?CreateThread(?lpThreadAttributes:?Pointer;?{安全設置}?dwStackSize:?DWORD;?lpStartAddress:?TFNThreadStartRoutine;??lpParameter:?Pointer;??dwCreationFlags:?DWORD;?var?lpThreadId:?DWORD? ):?THandle;?stdcall; CreateThread 的第一個參數 lpThreadAttributes 是指向 TSecurityAttributes 結構的指針, 一般都是置為 nil, 這表示沒有訪問限制; 該結構的定義是:
//TSecurityAttributes(又名:?SECURITY_ATTRIBUTES、_SECURITY_ATTRIBUTES)? _SECURITY_ATTRIBUTES?=?record?nLength:?DWORD;????????????????{結構大小}?lpSecurityDescriptor:?Pointer;?{默認?nil;?這是另一個結構?TSecurityDescriptor?的指針}?bInheritHandle:?BOOL;??????????{默認?False,?表示不可繼承}? end;? ? //TSecurityDescriptor(又名:?SECURITY_DESCRIPTOR、_SECURITY_DESCRIPTOR)? _SECURITY_DESCRIPTOR?=?record? ??Revision:?Byte;? ??Sbz1:?Byte;? ??Control:?SECURITY_DESCRIPTOR_CONTROL;? ??Owner:?PSID;? ??Group:?PSID;? ??Sacl:?PACL;? ??Dacl:?PACL;? end; 夠復雜的, 但我們在多線程編程時不需要去設置它們, 大都是使用默認設置(也就是賦值為 nil).

我覺得有必要在此刻了解的是: 建立系統內核對象時一般都有這個屬性(TSecurityAttributes);
在接下來多線程的課題中要使用一些內核對象, 不如先盤點一下, 到時碰到這個屬性時給個 nil 即可, 不必再費神.
{建立事件}? function?CreateEvent(?lpEventAttributes:?PSecurityAttributes;?{!}?bManualReset:?BOOL;?bInitialState:?BOOL;?lpName:?PWideChar? ):?THandle;?stdcall;?{建立互斥}? function?CreateMutex(? ??lpMutexAttributes:?PSecurityAttributes;?{!}? ??bInitialOwner:?BOOL;? ??lpName:?PWideChar? ):?THandle;?stdcall;? ? {建立信號}? function?CreateSemaphore(? ??lpSemaphoreAttributes:?PSecurityAttributes;?{!}? ??lInitialCount:?Longint;? ??lMaximumCount:?Longint;? ??lpName:?PWideChar? ):?THandle;?stdcall;? ? {建立等待計時器}? function?CreateWaitableTimer(? ??lpTimerAttributes:?PSecurityAttributes;?{!}? ??bManualReset:?BOOL;? ??lpTimerName:?PWideChar? ):?THandle;?stdcall;? 上面的四個系統內核對象(事件、互斥、信號、計時器)都是線程同步的手段, 從這也能看出處理線程同步的復雜性; 不過這還不是全部, Windows Vista 開始又增加了 Condition variables(條件變量)、Slim Reader-Writer Locks(讀寫鎖)等同步手段.

不過最簡單、最輕便(速度最快)的同步手段還是 CriticalSection(臨界區), 但它不屬于系統內核對象, 當然也就沒有句柄、沒有 TSecurityAttributes 這個安全屬性, 這也導致它不能跨進程使用; 不過寫多線程時一般不用跨進程, 所以 CriticalSection 應該是最常用的同步手段.

二、臨界區。
先看一段程序, 代碼文件:
unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs,?StdCtrls;?type?TForm1?=?class(TForm)? ????ListBox1:?TListBox;? ????Button1:?TButton;? ????procedure?FormCreate(Sender:?TObject);? ????procedure?Button1Click(Sender:?TObject);? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? function?MyThreadFun(p:?Pointer):?DWORD;?stdcall;? var? ??i:?Integer;? begin? ??for?i?:=?0?to?99?do?Form1.ListBox1.Items.Add(IntToStr(i));? ??Result?:=?0;? end;? ? procedure?TForm1.Button1Click(Sender:?TObject);? var? ??ID:?DWORD;? begin? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? end;? ? procedure?TForm1.FormCreate(Sender:?TObject);? begin? ??ListBox1.Align?:=?alLeft;? end;? ? end. 在這段程序中, 有三個線程幾乎是同時建立, 向窗體中的 ListBox1 中寫數據, 最后寫出的結果是這樣的:

能不能讓它們別打架, 一個完了另一個再來? 這就要用到多線程的同步技術.
前面說過, 最簡單的同步手段就是 "臨界區".

先說這個 "同步"(Synchronize), 首先這個名字起的不好, 我們好像需要的是 "異步"; 其實異步也不準確...
管它叫什么名字呢, 它的目的就是保證不沖突、有次序、都發生.

"臨界區"(CriticalSection): 當把一段代碼放入一個臨界區, 線程執行到臨界區時就獨占了, 讓其他也要執行此代碼的線程先等等; 這和前面用的 Lock 和 UnLock 差不多; 使用格式如下:
var?CS:?TRTLCriticalSection;???{聲明一個?TRTLCriticalSection?結構類型變量;?它應該是全局的}? InitializeCriticalSection(CS);?{初始化}? EnterCriticalSection(CS);??????{開始:?輪到我了其他線程走開}? LeaveCriticalSection(CS);??????{結束:?其他線程可以來了}? DeleteCriticalSection(CS);?????{刪除:?注意不能過早刪除}? ? //也可用?TryEnterCriticalSection?替代?EnterCriticalSection. 用上臨界區, 重寫上面的代碼, 運行效果圖:

//用臨界區重寫后的代碼文件:? unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs,?StdCtrls;?type? ??TForm1?=?class(TForm)? ????ListBox1:?TListBox;? ????Button1:?TButton;? ????procedure?FormCreate(Sender:?TObject);? ????procedure?FormDestroy(Sender:?TObject);? ????procedure?Button1Click(Sender:?TObject);? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? var? ??CS:?TRTLCriticalSection;? ? function?MyThreadFun(p:?Pointer):?DWORD;?stdcall;? var? ??i:?Integer;? begin? ??EnterCriticalSection(CS);? ??for?i?:=?0?to?99?do?Form1.ListBox1.Items.Add(IntToStr(i));? ??LeaveCriticalSection(CS);? ??Result?:=?0;? end;? ? procedure?TForm1.Button1Click(Sender:?TObject);? var? ??ID:?DWORD;? begin? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? end;? ? procedure?TForm1.FormCreate(Sender:?TObject);? begin? ??ListBox1.Align?:=?alLeft;? ??InitializeCriticalSection(CS);? end;? ? procedure?TForm1.FormDestroy(Sender:?TObject);? begin? ??DeleteCriticalSection(CS);? end;? ? end. Delphi 在 SyncObjs 單元給封裝了一個 TCriticalSection 類, 用法差不多, 代碼如下:
unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs,?StdCtrls;?type?TForm1?=?class(TForm)? ????ListBox1:?TListBox;? ????Button1:?TButton;? ????procedure?FormCreate(Sender:?TObject);? ????procedure?FormDestroy(Sender:?TObject);? ????procedure?Button1Click(Sender:?TObject);? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? uses?SyncObjs;? ? var? ??CS:?TCriticalSection;? ? function?MyThreadFun(p:?Pointer):?DWORD;?stdcall;? var? ??i:?Integer;? begin? ??CS.Enter;? ??for?i?:=?0?to?99?do?Form1.ListBox1.Items.Add(IntToStr(i));? ??CS.Leave;? ??Result?:=?0;? end;? ? procedure?TForm1.Button1Click(Sender:?TObject);? var? ??ID:?DWORD;? begin? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? ??CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);? end;? ? procedure?TForm1.FormCreate(Sender:?TObject);? begin? ??ListBox1.Align?:=?alLeft;? ??CS?:=?TCriticalSection.Create;? end;? ? procedure?TForm1.FormDestroy(Sender:?TObject);? begin? ??CS.Free;? end;? ? end. 三、等待函數 WaitForSingleObject
一下子跳到等待函數 WaitForSingleObject, 是因為下面的 Mutex、Semaphore、Event、WaitableTimer 等同步手段都要使用這個函數; 不過等待函數可不止 WaitForSingleObject 它一個, 但它最簡單.
function?WaitForSingleObject(?hHandle:?THandle;??????{要等待的對象句柄}?dwMilliseconds:?DWORD??{等待的時間,?單位是毫秒}? ):?DWORD;?stdcall;???????{返回值如下:}?WAIT_OBJECT_0??{等著了,?本例中是:?等的那個進程終于結束了}? WAIT_TIMEOUT???{等過了點(你指定的時間),?也沒等著}? WAIT_ABANDONED?{好不容易等著了,?但人家還是不讓咱執行;?這一般是互斥對象}? ? //WaitForSingleObject?的第二個參數一般給常數值?INFINITE,?表示一直等下去,?死等.?
WaitForSingleObject 等待什么? 在多線程里就是等待另一個線程的結束, 快來執行自己的代碼; 不過它可以等待的對象可不止線程; 這里先來一個等待另一個進程結束的例子, 運行效果圖:

//WaitForSingleObject的示例代碼文件:?unit?Unit1;?interface?uses?Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?Dialogs,?StdCtrls;?type? ??TForm1?=?class(TForm)? ????Button1:?TButton;? ????procedure?Button1Click(Sender:?TObject);? ??end;? ? var? ??Form1:?TForm1;? ? implementation? ? {$R?*.dfm}? ? var? ??hProcess:?THandle;?{進程句柄}? ? {等待一個指定句柄的進程什么時候結束}? function?MyThreadFun(p:?Pointer):?DWORD;?stdcall;? begin? ??if?WaitForSingleObject(hProcess,?INFINITE)?=?WAIT_OBJECT_0?then? ????Form1.Text?:=?Format('進程?%d?已關閉',?[hProcess]);? ??Result?:=?0;? end;? ? {啟動一個進程,?并建立新線程等待它的結束}? procedure?TForm1.Button1Click(Sender:?TObject);? var? ??pInfo:?TProcessInformation;? ??sInfo:?TStartupInfo;? ??Path:?array[0..MAX_PATH-1]?of?Char;? ??ThreadID:?DWORD;? begin? ??{先獲取記事本的路徑}? ??GetSystemDirectory(Path,?MAX_PATH);? ??StrCat(Path,?'\notepad.exe');? ? ??{用?CreateProcess?打開記事本并獲取其進程句柄,?然后建立線程監視}? ??FillChar(sInfo,?SizeOf(sInfo),?0);? ??if?CreateProcess(Path,?nil,?nil,?nil,?False,?0,?nil,?nil,?sInfo,?pInfo)?then? ??begin? ????hProcess?:=?pInfo.hProcess;???????????????????????????{獲取進程句柄}? ????Text?:=?Format('進程?%d?已啟動',?[hProcess]);?? ????CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ThreadID);?{建立線程監視}? ??end;? end;? ? end.

轉載于:https://www.cnblogs.com/guorongtao/p/4390310.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的delphi 中几种多线程操作方式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

日韩欧美一区二区在线 | 色综合久久88色综合天天6 | 免费视频91 | 免费合欢视频成人app | 99精品免费在线观看 | 97香蕉久久超级碰碰高清版 | 日韩精品中文字幕在线 | 色欧美88888久久久久久影院 | a黄色片 | 久久久久久久久久久久av | 亚洲国产精品人久久电影 | 精品999国产| 一区三区在线欧 | 欧美午夜视频在线 | 欧美另类一二三四区 | 激情综合色播五月 | 午夜在线免费视频 | 久草免费福利在线观看 | 久久国产精品免费一区 | 少妇bbw撒尿 | 成人av电影免费在线观看 | 亚洲综合色播 | 在线观看中文字幕 | 婷婷色中文 | 99久久精品免费视频 | 中文字幕亚洲精品日韩 | 香蕉视频在线免费 | 99理论片 | 成人a免费视频 | 一级a毛片高清视频 | 激情丁香 | 精品久久久久久国产91 | 黄色一区二区在线观看 | 天天曰天天 | 草久视频在线观看 | 午夜美女视频 | 高清中文字幕 | 国产91成人 | 国产精品一区二区久久 | 狠狠操夜夜操 | 欧美国产三区 | 免费av影视 | 亚洲精品午夜久久久久久久久久久 | 中文av影院| 六月天综合网 | 九九免费精品视频 | 天天色播 | av在线a | 天天干天天草天天爽 | 国产一区二区三区在线免费观看 | 久久综合久久综合这里只有精品 | 国产va精品免费观看 | 免费黄色在线网址 | 中文成人字幕 | 国产成人一区二区三区在线观看 | 成人免费观看网址 | 亚洲影视资源 | 91热爆视频 | 国产一区二区观看 | 国产精品成人免费一区久久羞羞 | 日日草夜夜操 | 中文字幕国语官网在线视频 | 亚洲国产精品免费 | 国产成人精品午夜在线播放 | 成人免费网站在线观看 | 91久久人澡人人添人人爽欧美 | 99久久精品国 | 久草网视频在线观看 | 日日草天天干 | 天天做天天干 | 999久久久| 欧美日韩一区二区三区在线观看视频 | 一区二区三区手机在线观看 | 国产精品高清在线观看 | 国产精品成久久久久三级 | 亚洲精品一区二区18漫画 | 亚洲精品国产第一综合99久久 | 久草网站在线观看 | 激情欧美xxxx| 中文在线免费视频 | 国产精品2区 | 黄色动态图xx | 日韩三级视频在线观看 | 日韩激情视频在线观看 | 色婷婷久久久综合中文字幕 | 99久久www| 黄色在线网站噜噜噜 | 国产中文字幕一区二区 | 91九色最新地址 | 午夜精品久久久久久中宇69 | 97人人艹| 四虎免费av | 手机看片1042 | 国产电影黄色av | 婷婷在线精品视频 | 亚洲草视频 | 国产亚洲成av片在线观看 | 久久国产区 | 日本久久久久 | 久久精品久久99精品久久 | 欧美精品资源 | 日本超碰在线 | 99热这里只有精品8 久久综合毛片 | 久久黄色精品视频 | 中文字幕人成乱码在线观看 | 高清中文字幕 | 黄色免费观看 | 国产女人免费看a级丨片 | 香蕉视频导航 | 黄色小网站在线观看 | 国产精品成人久久久 | 欧美-第1页-屁屁影院 | 精品不卡av| 日韩久久精品一区 | 精品一区二区三区在线播放 | 91成熟丰满女人少妇 | 啪啪肉肉污av国网站 | 国产少妇在线观看 | 一区二区视| 国产精品乱码久久久久久1区2区 | 在线99热 | 国产一区二区精品久久91 | 一区二区三区久久精品 | 韩日av在线 | 91视频 - v11av| 国产精品欧美精品 | 麻豆成人小视频 | 免费日韩一区二区三区 | 久久国产免费看 | 色激情在线| 在线亚洲高清视频 | 欧美性色综合 | 五月婷婷视频在线观看 | 亚洲综合激情 | 中文字幕久久网 | 99超碰在线观看 | 四虎在线视频免费观看 | 天天做天天看 | 国产精品一区二区久久国产 | 韩国视频一区二区三区 | 五月亚洲| 国产偷在线 | 国产短视频在线播放 | 日日摸日日添夜夜爽97 | 欧美精品久久久久久久久老牛影院 | 久久草在线精品 | av电影在线不卡 | 有码中文字幕在线观看 | 91成版人在线观看入口 | 亚洲综合成人专区片 | 中文在线最新版天堂 | 久久好看 | 97超视频在线观看 | 久久激情五月婷婷 | 天天操天天操 | 丁香九月婷婷 | 深爱激情五月网 | 久久亚洲视频 | 天天操夜夜操天天射 | 日韩影片在线观看 | a黄色片在线观看 | 欧美日韩三级 | 免费看在线看www777 | 欧美激情视频一区二区三区 | av一区二区三区在线观看 | 欧美色图p | 国产精品成人在线观看 | 91亚色视频 | 亚洲综合视频网 | av九九 | 日韩欧美高清视频在线观看 | 国产精品久久久久影院 | 91色网址 | 国产精品久久久久久久久费观看 | 伊人在线视频 | 国产成人精品一区二区三区 | 在线观看视频三级 | 久久av福利 | 日韩三区在线观看 | 国产精品一区二区三区免费视频 | 最新av中文字幕 | 中文字幕在线观看资源 | 亚洲精品乱码久久久久久久久久 | 欧美亚洲一区二区在线 | 亚洲九九九在线观看 | 国产日韩精品一区二区三区 | 国产免费亚洲高清 | 久草在线免费看视频 | 超碰在线人人爱 | 色综合久久久 | 91av社区| 日韩精品极品视频 | 亚洲jizzjizz日本少妇 | 国产精品午夜在线 | 欧美老少交 | 午夜av影院 | 99视频在线观看一区三区 | 亚洲国产精品va在线看 | 日韩中文在线字幕 | 综合色久 | 天天操天天操天天操天天操天天操天天操 | 久久99九九99精品 | 日韩成人在线一区二区 | 狠狠色丁香九九婷婷综合五月 | 97国产精品亚洲精品 | 人人爽人人爽 | 欧美va在线观看 | 国产综合小视频 | 亚洲男模gay裸体gay | 国产福利91精品张津瑜 | 日本三级不卡视频 | 成人在线视频在线观看 | 人人爱人人爽 | 激情欧美在线观看 | 免费高清在线视频一区· | 亚洲男男gⅴgay双龙 | 91久久国产综合精品女同国语 | 九九久久免费视频 | 99国产视频| 激情欧美一区二区三区免费看 | 亚洲 欧美 日韩 综合 | av资源网在线播放 | 天天玩天天操天天射 | 亚洲三级精品 | 欧美不卡视频在线 | 久久久精品一区二区三区 | 国产亚洲精品久久久久久久久久久久 | 99re热精品视频 | 在线观看播放av | 男女拍拍免费视频 | 99国产视频在线 | 99久久精品免费一区 | 视频在线99re | 国产xxxx做受性欧美88 | 免费看成人 | 亚洲国产欧美一区二区三区丁香婷 | 午夜av片 | 91免费国产在线观看 | 在线亚洲播放 | 久青草视频在线观看 | 狠狠色狠狠色 | 久要激情网 | 亚洲国产三级在线 | 亚洲精品一区二区久 | 99视频在线免费播放 | 久久精品激情 | 成人黄色小说网 | 日韩电影在线观看中文字幕 | 国产短视频在线播放 | 91九色porny蝌蚪视频 | 日韩网站一区二区 | 夜夜躁日日躁狠狠久久av | 国产视频在线一区二区 | 久久精品国产99 | 日韩激情免费视频 | 中文 一区二区 | 免费在线观看不卡av | 色综合激情网 | 久久成人久久 | 欧美久久久久久久久久 | 精品人妖videos欧美人妖 | 91精品久久久久久久99蜜桃 | 999成人 | 伊人导航 | 免费三级骚 | 18国产精品白浆在线观看免费 | 伊人狠狠 | 国产.精品.日韩.另类.中文.在线.播放 | 色综合中文综合网 | 久久久久北条麻妃免费看 | 日本性高潮视频 | 人人精久 | 激情网站网址 | www.色五月| 亚洲视频 一区 | 人人爽人人爱 | 国产青春久久久国产毛片 | 亚洲一区二区91 | 久久视频免费在线观看 | 国产精品自在线 | 99国产情侣在线播放 | 激情综合五月天 | 久久精品一区 | 国产五月色婷婷六月丁香视频 | 精品在线你懂的 | 婷婷av资源 | 麻豆国产精品永久免费视频 | 国产精品对白一区二区三区 | 欧美成人性战久久 | 国产精品久久99 | 国产中文在线字幕 | 久久久久久久久毛片 | 五月激情站 | 中文字幕高清在线播放 | www日日| 久要激情网 | 午夜精品一区二区三区免费 | 欧美另类交在线观看 | 超碰在线97观看 | 国产高清在线免费观看 | 国内成人精品2018免费看 | 日韩av一区二区三区在线观看 | 久久99久久99精品免观看粉嫩 | 99久高清在线观看视频99精品热在线观看视频 | 一级国产视频 | 丰满少妇高潮在线观看 | 久久久 精品 | 亚洲91中文字幕无线码三区 | 精品国产欧美一区二区 | 91毛片视频 | 少妇搡bbb | 午夜少妇一区二区三区 | 亚洲色图 校园春色 | 五月天色站 | 中日韩免费视频 | 综合国产视频 | www.少妇 | 91精彩视频在线观看 | 国产精品美女久久久久久久久 | 国产精品综合久久 | 少妇啪啪av入口 | 久久久久久久久久久网站 | 成人国产电影在线观看 | 婷婷在线免费观看 | 亚洲91精品在线观看 | 日日夜夜精品视频 | 国产色女 | 黄色一区二区在线观看 | 久久精品视频播放 | 亚洲国产中文字幕 | www.99在线观看 | 中文字幕精品三区 | 狠狠色网| 69国产盗摄一区二区三区五区 | www.黄色在线 | 亚洲欧美国内爽妇网 | 亚洲精品中文字幕在线观看 | 三级av免费 | 亚洲国产人午在线一二区 | 手机av观看| 超碰97在线资源 | 人人插人人爱 | 射射色 | 99精品在线观看 | 青草视频在线免费 | 国产精品国产三级国产aⅴ无密码 | 日本xxxx裸体xxxx17 | 国产精品va在线播放 | 人人干人人搞 | 亚洲精品乱码久久久久久写真 | 国产一级二级在线播放 | 少妇自拍av | 天堂在线一区二区 | 亚洲精品乱码久久久久久写真 | 天天色综合久久 | 色久网| 国产夫妻性生活自拍 | 久久久久夜色 | 17婷婷久久www | 国产精品一区二区三区电影 | 免费欧美高清视频 | 国产精品99久久免费观看 | 午夜精品久久久久久久99 | 亚州性色 | 成人av电影网址 | www.夜夜草 | 91色国产| avlulu久久精品 | 免费三级黄 | 黄色1级毛片 | 欧美性生活免费看 | 91成人欧美| 亚洲网久久 | 日韩免费视频在线观看 | 日本护士三级少妇三级999 | 91麻豆国产福利在线观看 | 最新成人在线 | 伊人色综合久久天天 | 九九久久久久久久久激情 | 天天操比 | 国偷自产中文字幕亚洲手机在线 | 日韩美视频 | av免费观看在线 | 欧美专区亚洲专区 | 国产免费嫩草影院 | 蜜臀av性久久久久av蜜臀三区 | 久久久久看片 | 国产又粗又硬又长又爽的视频 | 97超级碰 | 992tv在线成人免费观看 | 美女国产在线 | 国产精品久久久网站 | 国产一区二区高清视频 | 亚洲精品国产精品99久久 | 亚洲精品自在在线观看 | 欧美人交a欧美精品 | 久99久精品视频免费观看 | 久精品视频在线 | 国产做aⅴ在线视频播放 | av在线影片| 美女一二三区 | 亚洲综合视频在线 | 麻豆一二三精选视频 | 青青河边草免费观看完整版高清 | 天天舔天天射天天操 | 久久综合狠狠综合 | 亚洲成人精品国产 | 日韩在线字幕 | 永久免费av在线播放 | 精品免费视频123区 午夜久久成人 | 综合网婷婷 | 91片网 | 亚洲人成精品久久久久 | 成人app在线播放 | 国产超碰在线观看 | 少妇bbbb搡bbbb桶 | av天天澡天天爽天天av | 亚洲精品乱码久久久久久久久久 | 中文字幕第一页av | 丁香视频免费观看 | 最新国产精品亚洲 | 韩日在线一区 | 日韩视频在线不卡 | 在线看中文字幕 | 亚洲精品视频在线看 | 国产黄色片在线 | 98精品国产自产在线观看 | 在线日本看片免费人成视久网 | 99精品国产一区二区三区麻豆 | 波多野结衣一区二区 | 在线视频中文字幕一区 | 夜夜夜夜爽 | 日韩av在线不卡 | 国产精品久久久一区二区 | 久久综合久久久久88 | 五月婷婷丁香综合 | 国产免费观看久久黄 | 久久视影 | 国产精品99久久久久久久久久久久 | 麻豆视频国产在线观看 | 国产福利电影网址 | 一区二区三区在线播放 | 色综合久久精品 | 黄色av电影 | 伊人婷婷久久 | 中文字幕一区二区三区在线观看 | av黄免费看 | 国产免费不卡av | 久久免费av电影 | 99久久99久久综合 | 欧美另类激情 | 成人av在线直播 | 日韩久久久 | 午夜精品久久久久久久99水蜜桃 | 国产精品麻豆果冻传媒在线播放 | 免费亚洲视频在线观看 | 激情 一区二区 | 国产精品av免费 | 久久短视频 | 丝袜足交在线 | 亚洲国产精品va在线看黑人 | 99久久精品无码一区二区毛片 | 在线国产日韩 | 免费一级黄色 | 黄色日批网站 | 人人爽人人爽人人片av免 | 中文资源在线官网 | 蜜臀av性久久久久av蜜臀妖精 | 欧美一级xxxx | 亚洲成人av电影 | 激情视频久久 | 久久久视屏 | 99久久久国产精品 | 国产亚州精品视频 | 欧美综合国产 | 亚洲一级片 | 亚州成人av在线 | 婷婷丁香激情 | 四川妇女搡bbbb搡bbbb搡 | 黄色网在线播放 | 亚洲不卡在线 | 激情在线免费视频 | 免费黄色av电影 | 久精品视频在线 | 久久国产美女视频 | 国内精品久久久久 | 97精品超碰一区二区三区 | 国产视频资源在线观看 | 四虎永久免费网站 | 国产精品va最新国产精品视频 | 天天草天天插 | 干干干操操操 | 美女免费视频一区 | 免费看黄网站在线 | 中文字幕日韩精品有码视频 | 国产精品久久久久影院日本 | 国产91区| 中文字幕欧美激情 | 天堂av免费观看 | 五月天婷婷丁香花 | 狠狠狠狠狠操 | av成人在线网站 | 久久99这里只有精品 | 69国产成人综合久久精品欧美 | 激情小说 五月 | 日日夜夜人人天天 | 日韩精品一区二区免费视频 | 亚洲色五月| 狠狠色丁香久久综合网 | 色婷婷亚洲精品 | 国产精品视频资源 | 国产精品精品久久久 | 精品久久久久久久久亚洲 | 2021国产精品 | 99r在线精品 | 国产精品久久久99 | 成人免费xyz网站 | 嫩草伊人久久精品少妇av | 久草在线资源观看 | 婷婷资源站 | 国产不卡免费视频 | av丁香 | 亚洲激情在线观看 | 伊人婷婷网 | 五月婷婷久久综合 | 992tv人人网tv亚洲精品 | 精品在线视频一区二区三区 | 日韩免费视频 | 久草视频在线免费看 | 久久九精品 | 日韩美女黄色片 | 制服丝袜成人在线 | 久久综合色天天久久综合图片 | 国产小视频免费在线观看 | 日韩激情免费视频 | 亚洲成色777777在线观看影院 | 97视频人人澡人人爽 | 伊人狠狠色 | 色婷婷av国产精品 | 国产精品一区二区三区99 | 国产一区二区久久精品 | 97视频在线观看成人 | 日韩免费成人av | 美女视频a美女大全免费下载蜜臀 | 97国产大学生情侣白嫩酒店 | 不卡的av电影| 91色亚洲 | 欧美日本在线视频 | 日本狠狠色 | 国产传媒一区在线 | 欧美日韩高清免费 | 日日夜夜精品免费 | 正在播放久久 | 亚洲九九精品 | 成人a在线观看高清电影 | 午夜丁香视频在线观看 | 国产亚洲视频在线 | 日韩av电影国产 | 综合久久五月天 | 黄av在线 | 91麻豆精品国产午夜天堂 | 国产96视频 | 三级动态视频在线观看 | 字幕网av | 国产亚洲精品免费 | 精品视频123区在线观看 | 一级成人网 | av黄色国产 | 国产伦精品一区二区三区免费 | 国产精品videossex国产高清 | 成人高清在线观看 | 高清视频一区 | 91免费的视频在线播放 | 综合色天天 | 超碰97免费 | 在线看黄色av| 亚洲精品tv久久久久久久久久 | 成人小视频免费在线观看 | 久久一精品 | 色婷婷天天干 | 欧美激情精品久久久久久 | 99久久激情| 91最新网址 | 成人9ⅰ免费影视网站 | 免费av在线网 | 亚洲午夜精品一区二区三区电影院 | 日本在线视频网址 | 免费亚洲黄色 | 日本久久久久久久久久久 | 成人va在线观看 | 国产日本亚洲 | 精品在线一区二区三区 | av色综合网 | 97人人艹 | 男女拍拍免费视频 | 久久精品国产亚洲精品 | 日韩在线观看一区二区 | 国产一区二区视频在线 | 日韩av视屏在线观看 | 亚洲理论电影 | 天海冀一区二区三区 | 国产在线不卡精品 | 国产成人精品av久久 | 日日日操操 | 精品在线视频一区 | 一区二区精品在线观看 | 久久99久久99 | 久久久91精品国产 | 成人理论在线观看 | 国产精品mv | 99视频在线精品免费观看2 | 国产在线传媒 | 久久久久久久久毛片精品 | 色五丁香 | 在线国产视频观看 | 蜜臀av夜夜澡人人爽人人桃色 | 97精品国产一二三产区 | 99视频在线观看免费 | 久久激情网站 | 日韩午夜高清 | 精品国产一区二区三区不卡 | 91黄色影视 | 久久九九免费 | 国产视频一 | 国产精品成人在线 | 中国一级片视频 | 国产在线最新 | 在线观看免费福利 | 久草在线资源观看 | 日本一区二区高清不卡 | 国产中文字幕三区 | 精品国产aⅴ一区二区三区 在线直播av | 国产精品18久久久久久久 | 欧美 激情 国产 91 在线 | 国产精品不卡在线播放 | 中文字幕中文字幕在线一区 | 国产黄a三级三级 | 亚洲色影爱久久精品 | 91自拍视频在线观看 | 亚洲精品乱码白浆高清久久久久久 | aa一级片| 欧美吞精| 国产亚洲在线观看 | 国产精品一区二区 91 | 日韩精品一区二区免费视频 | 日韩欧美视频一区 | 欧美日韩视频观看 | 亚洲激情视频 | 最新日韩视频 | 波多野结衣资源 | 亚洲午夜久久久综合37日本 | 国产精品美女久久久久aⅴ 干干夜夜 | 欧美综合在线视频 | 狠狠操狠狠干2017 | 蜜臀av性久久久久蜜臀aⅴ涩爱 | 美女视频黄免费的久久 | 香蕉97视频观看在线观看 | 成人黄色一级视频 | 国产在线观看不卡 | 国产精品一区二区三区在线看 | 国产偷国产偷亚洲清高 | 国产 视频 高清 免费 | 亚洲精品乱码久久久久 | 97在线免费视频 | 日韩网站在线 | 一区二区视频在线免费观看 | 国产1级视频 | 伊人资源视频在线 | 欧美日韩免费视频 | 一区二区精 | 国产精品毛片一区视频 | 久久99精品久久久久久秒播蜜臀 | 午夜久久网站 | 网址你懂的在线观看 | 久久久精品免费观看 | 在线电影中文字幕 | 午夜精品在线看 | 2019中文在线观看 | 97视频免费在线 | 亚洲婷婷综合色高清在线 | 91视频黄色 | 国内精品久久久久久久久久清纯 | 中文在线天堂资源 | 99高清视频有精品视频 | 一区在线免费观看 | 久久精品99国产国产 | 91福利视频免费 | 久久九九国产视频 | 在线视频观看成人 | 国产小视频免费在线观看 | 白丝av在线 | 天天综合在线观看 | 欧美a级在线免费观看 | 日韩在线免费不卡 | 久久精品欧美日韩精品 | 欧美一区二区三区在线看 | 韩国av免费看| 亚洲精品小视频在线观看 | 黄色网址a | 91大神dom调教在线观看 | 久草影视在线观看 | 一区二区三区免费在线观看 | 久草观看 | 亚洲涩涩涩涩涩涩 | 免费观看成人网 | 久久婷亚洲五月一区天天躁 | 中文免费观看 | 在线观看国产一区二区 | 男女靠逼app | 亚洲va韩国va欧美va精四季 | 九九在线国产视频 | 免费日韩一区二区 | 国产一区视频在线播放 | 91麻豆精品91久久久久同性 | 国产一级在线播放 | 精品久久久久久久久亚洲 | 久久综合影音 | 成人黄色小说视频 | 亚洲成人av片在线观看 | 国产永久免费高清在线观看视频 | 日韩网站免费观看 | 成人a视频 | 亚洲精品中文字幕视频 | 激情综合啪啪 | 456免费视频 | 麻豆视传媒官网免费观看 | h视频在线看 | 日本少妇高清做爰视频 | 国产高清免费观看 | 国产中文字幕三区 | 99国产精品一区 | 久久综合色婷婷 | www.日日日.com | 久久久久欧美精品 | 91av蜜桃 | 高清视频一区二区三区 | 韩国av不卡| 97色噜噜| 国产免费观看高清完整版 | 综合婷婷| 国产成人免费在线 | 美女久久网站 | 97在线观看免费高清完整版在线观看 | 成人av日韩 | 中文字幕在线观看免费高清电影 | 欧美国产日韩在线视频 | 日本久久中文 | 日韩美女免费线视频 | 91精品一区二区三区久久久久久 | 天天视频色 | 中文字幕在线观看免费高清电影 | 国产视频不卡一区 | 国产免费观看久久黄 | 四月婷婷在线观看 | 亚洲涩涩涩 | 国产一级精品视频 | 中文字幕电影高清在线观看 | www免费在线观看 | 日韩r级电影在线观看 | 久久精品中文字幕一区二区三区 | 91麻豆精品久久久久久 | 亚洲免费色 | 国产h片在线观看 | 香蕉在线观看视频 | 久久夜色精品国产欧美乱 | 人人狠狠综合久久亚洲婷 | 在线观看视频一区二区 | 色综合久久久久 | 国内成人精品2018免费看 | 亚洲无在线 | 日韩欧美有码在线 | 九九精品在线观看 | 国产精品女 | 日本在线视频网址 | 日韩在线免费视频 | 天天操天天弄 | 国产电影一区二区三区四区 | 一区二区在线电影 | 99这里精品 | 中文av在线天堂 | 亚洲精品久久久久久久不卡四虎 | 天天夜夜亚洲 | 国产裸体视频网站 | 国产美女被啪进深处喷白浆视频 | 91资源在线免费观看 | 亚洲欧美视频在线播放 | av电影一区二区 | 国产玖玖精品视频 | 日日爽日日操 | 亚洲综合色视频 | 午夜精品久久久99热福利 | 国产精品毛片久久久 | 国产一区不卡在线 | 91视频3p| 五月色丁香 | 国产精品99久久免费黑人 | 久久丝袜视频 | 成人国产精品av | 黄网站色视频免费观看 | 国产一区二区影院 | 免费黄色av | 99精品免费久久久久久久久日本 | 激情五月婷婷激情 | 五月开心六月伊人色婷婷 | 91色网址 | 手机成人av| 欧美黄网站 | 黄色成人av在线 | 999热线在线观看 | 在线观看国产永久免费视频 | 美女久久久久久 | 国产中文在线视频 | 久久精品男人的天堂 | 日韩精品一区二 | 亚洲电影久久久 | 园产精品久久久久久久7电影 | 狠狠躁18三区二区一区ai明星 | 又色又爽又黄高潮的免费视频 | 亚洲日本精品视频 | 伊人婷婷激情 | 香蕉久久久久久久 | 九九视频免费 | www.国产高清 | 日韩精品视频免费专区在线播放 | 五月婷婷综合激情网 | 亚洲综合视频在线播放 | 黄色com | 99超碰在线播放 | 黄色av一区 | 亚洲影院国产 | 天天干天天草天天爽 | 日本中文字幕在线看 | 97中文字幕 | 天天爽天天射 | 亚洲免费不卡 | 久久激五月天综合精品 | 97人人视频| av久久久久久 | 国产免费又粗又猛又爽 | 最新色视频 | 亚洲精品视频免费在线观看 | 日本韩国欧美在线观看 | 最近字幕在线观看第一季 | 国产一区二区电影在线观看 | 日韩成人不卡 | 日日碰狠狠添天天爽超碰97久久 | 91资源在线 | 国产精品久久久久久久久婷婷 | 超碰日韩 | 国产精品成人一区二区 | 日日夜夜天天综合 | 韩国精品一区二区三区六区色诱 | 日日干天夜夜 | 亚洲综合视频在线播放 | 美女视频永久黄网站免费观看国产 | 欧美日韩国内在线 | 五月婷婷伊人网 | 99爱视频 | 九九精品视频在线看 | 亚洲成aⅴ人在线观看 | 久影院 | 亚洲高清在线观看视频 | 久久久精品午夜 | 色国产精品一区在线观看 | 日日夜夜噜 | 免费亚洲视频在线观看 | 免费视频久久 | 人人dvd| 国产日韩精品一区二区三区在线 | 97视频在线免费播放 | 免费看一级黄色 | 欧美激情综合五月色丁香 | 国产麻豆视频在线观看 | 九九九九免费视频 | 在线欧美中文字幕 | 狠狠干网站 | 日韩午夜电影网 | 欧美一级久久久久 | 精品亚洲成a人在线观看 | 日本少妇高清做爰视频 | 日韩精品久久久久久 | 麻豆播放 | 精品视频一区在线 | 色视频在线免费观看 | 首页av在线| av理论电影 | 日韩激情影院 | 欧美日韩在线视频一区 | 亚洲在线黄色 | 午夜日b视频 | 亚洲精品综合在线观看 | 中文av网 | 91女子私密保健养生少妇 | 日韩视频专区 | 日日天天狠狠 | 日韩精品久久久久久久电影竹菊 | 亚洲黄在线观看 | 久久五月婷婷综合 | 国产91精品看黄网站在线观看动漫 | 日韩色在线 | 日韩精品视频久久 | 狠狠色丁香婷婷综合视频 | 久久久九色精品国产一区二区三区 | 日韩视频一二三区 | 亚洲日日射 | 国产精品99久久99久久久二8 | 欧美人人 | 亚洲 成人 欧美 | www.久久99 | 91禁在线看 | 精品国产精品国产偷麻豆 | 五月婷婷av | 在线观看一区二区精品 | 欧美精品久久久久 | 国产丝袜一区二区三区 | 欧美va天堂va视频va在线 | 伊人色**天天综合婷婷 | 天天干,夜夜爽 | 国产一级二级视频 | 日一日操一操 | 欧美一级免费片 | 在线免费观看一区二区三区 | 在线观看免费色 | 国产做aⅴ在线视频播放 | 国产精品免费在线播放 | 中文字幕亚洲情99在线 | 在线观看av中文字幕 | 中文字幕中文中文字幕 | 精品国产一区二区三区久久久久久 | 欧美va天堂va视频va在线 | av最新资源 | 91最新在线视频 | 亚洲aaa级 | 成人黄大片视频在线观看 | 国产午夜精品一区 | 免费久草视频 | 亚洲精品视频在线观看网站 | 日韩国产精品久久久久久亚洲 | 免费av福利 | 日韩激情影院 | 操操色| 99在线观看免费视频精品观看 | a在线视频v视频 | 国产三级av在线 | 久久久性| 久草免费新视频 | 日韩精品 在线视频 | 天天曰夜夜爽 | 色综合久久综合中文综合网 | 成年人电影毛片 | 日本激情视频中文字幕 | 人人澡人摸人人添学生av | 久久综合操 | 久久涩视频 | 日本黄色免费观看 | 99视频播放 | 亚洲电影成人 | 欧美精品午夜 | 又黄又爽又湿又无遮挡的在线视频 | 日韩av网页| 日日操天天操夜夜操 | 国产无遮挡猛进猛出免费软件 | 国产精品久久久久免费 | 中文字幕中文字幕在线中文字幕三区 | 国产黄a三级 | 欧美日韩在线免费视频 | 国产免费久久精品 | 免费高清在线观看电视网站 | 国产免费又爽又刺激在线观看 | 婷婷亚洲综合五月天小说 | 又黄又爽又刺激视频 | 亚洲人人精品 | 精品99免费视频 | 国产精品午夜久久 | 色婷婷亚洲综合 | 天天天天天天操 | 91精选在线 | 久久午夜精品影院一区 | 欧美中文字幕第一页 | h文在线观看免费 | 色综合网在线 | 亚洲高清视频在线 | 欧美性色xo影院 | 日韩欧美视频免费观看 | 五月婷婷久久综合 | 婷婷丁香六月 | 992tv成人免费看片 | 日韩在线视 | 国产成人精品一区二区三区福利 | 欧美性生活免费 | 亚洲国产高清在线观看视频 | 干av在线| 九九热中文字幕 | a天堂在线看 | 黄色毛片观看 | 久久精品免费电影 | 国产丝袜一区二区三区 | 欧美精彩视频在线观看 |