デルファイのスレッドクラス - (1)
デルファイのスレッドクラス - (1)ラプター(オリジナル作品)
キーワードThreadEventCriticalSectionSynChronize
デルファイのスレッドクラス
ラプターズ[メンタルストゥディオ]
http://mental.mentsu.com
(1つ)
Delphiには、マルチスレッドプログラミングの実装に使用されるスレッドクラスがありますが、基本的にはTthreadクラスの複数のメンバーに簡単な紹介を行い、その実行と使用量を説明します。同期が終了します。ただし、これはマルチスレッドプログラミングの全体ではありません。
スレッドは、基本的にプロセスで同時に実行されるコードの一部です。プロセスには、少なくとも1つのスレッド、いわゆるメインスレッドがあります。複数の子スレッドもあります。プロセスで複数のスレッドを使用する場合、「マルチスレッド」と呼ばれます。
それでは、このいわゆる「コードの一部」がどのように定義されているのでしょうか?実際には関数またはプロセスです(Delphiの場合)。
Windows APIを使用してスレッドを作成すると、createThreadと呼ばれるAPI関数を介して実装されます。これは次のように定義されています。
HandleAteThread(
lpsecurity_attributeslpthreadattributes、
dworddwstacksize、
lpthread_start_routinelpstartaddress、
lpvoidlpparameter、
dworddwcreationflags、
lpdwordlpthreadid
);
それらのパラメーターは、名前で言及されているように、つまり、スレッド属性(NTの下でスレッドセキュリティ属性を設定するために使用され、9x未満の無効)、スタックサイズ、開始アドレス、パラメーター、および作成フラグ(作成時にステータスを設定するためにスレッドを設定するために使用されます) 、スレッドID、そして最後にスレッドハンドルを返します。開始アドレスは、スレッド関数の入り口であり、スレッド関数が終了するまでスレッドが終了します。
スレッド全体の実行プロセスは次のとおりです。
CreateThreadパラメーターは多くであり、Windows APIであるため、一般的なスレッド関数はCruntimelibraryで提供されます(理論的には、スレッドをサポートする任意のOSで使用できます):
unsignedlong_beginthread(void(_userentry*__ start)(void*)、unsigned__stksize、void*__ arg);
Delphiは、同じ関数を持つ同様の関数も提供します。
functionBeginThread(securityAttributes:pointer; stacksize:longword; swreadfunc:tthreadfunc; parineter:pointer; creationflags:longword; varthreadid:longword):integer;
これらの3つの関数の関数は、基本的に同じです。スレッド関数と一般的な関数の最大の違いは、スレッド関数が開始されるとすぐに、これらの3つのスレッドが戻り、メインスレッドは引き続き、スレッド関数が独立したスレッドで実行されることです実行するには、戻ってくるとき、メインスレッドはそれを気にしないか、それを知らないものです。
通常の状況では、スレッド関数が戻った後、スレッドが終了します。しかし、他の方法があります:
WindowsAPI:
voidexitthread(dworddwexitcode);
Cruntimelibrary:
void_endthread(void);
Delphiruntimelibrary:
procedureEndThread(exitcode:integer);
必要なスレッドデータ(状態/プロパティなど)を記録するために、OSはスレッドの内部オブジェクトを作成します。スレッドは終了します。
マルチスレッドプログラミングは、APIまたはRTL(RuntimeLibrary)を使用して簡単に実行できますが、この理由でより詳細な処理が必要です。
また、このクラスを使用するのは非常に簡単です。ほとんどのDelphiの本は、最初にTTHREADから独自のスレッドクラスを導き出すと言います(TTHREADは抽象クラスであり、インスタンスを生成できないため)。これは、スレッドで実行されるコードパーツであるスレッド関数です。これに関する具体的な詳細については、ここでは繰り返されません。関連する本を参照してください。
次に、この記事では、TTHREADクラスがスレッドをカプセル化する方法について説明します。つまり、TTHREADクラスの実装に関する詳細な調査を実施します。あなたが本当にそれを理解しているなら、それを使用する方が良いからです。
以下は、Delphi7のTthreadクラスの宣言です(この記事では、Windowsプラットフォームの下での実装のみについて説明するため、Linuxプラットフォームパーツに関するすべてのコードが削除されます):
tthread = class
プライベート
ファンドル:タンドル;
fthreadid:thandle;
fcreateSuspeded:boolean;
f端:ブール;
fsuspended:boolean;
ffreeOnterminate:boolean;
finished:boolean;
FRETURNVALUE:整数;
Fonterminate:tnotifyevent;
fsynchronize:tsynchronizercord;
ffatalexception:tobject;
ProcedureCallonTerminate;
classprocedureynchronize(asyncrec:psynchronizerecord);
functiongetpriority:tthreadpriority;
procedureSetPriority(Value:tthreadPriority);
手順(value:boolean);
保護されています
procedureCheckThreadError(errcode:integer); overload;
procedureCheckThreadError(成功:boolean);オーバーロード;
procesturedOterminate; virtual;
procedureExecute; virtual; abstract;
手順(方法:tthreadmethod);オーバーロード;
PropertyReturnValue:IntegerReadFreturnValueWriteFreturnValue;
プロパティ終了:booleanreadFerminated;
公共
constructorcreate(createSuspended:boolean);
Destructordestroy;オーバーライド;
手続きafterconstruction;オーバーライド;
ProcedurerSume;
procedureSuspend;
Procedureterminate;
functionwaitfor:longword;
classprocedureynchronize(athread:tthread; amethod:tthreadmethod); overload;
classProceDurestaticynChronize(athread:tthread; amethod:tthreadmethod);
PropertyFatalException:tobjectReadffatalException;
PropertyFreeOnTerminate:booleanreadffreeOnterminationwriteffreeOnterminate;
プロパティハンドル:thandlereadfhandle;
プロパティプリアリティ:tthreadpriorityreadgetPriorityWriteSetPriority;
suspeded:booleanreadfsuspendedwriteStesspended;
PropertyThreadID:thandlereadfthreadid;
PropertyOnterminate:tnotifyEventReadFonterminationWriteFonterminate;
終わり;
Tthreadクラスは、Delphi RTLの比較的単純なクラスであり、クラスの属性は非常にシンプルで明確です。
(つづく)
デルファイのスレッドクラス - (2)
デルファイのスレッドクラス - (2)ラプター(オリジナル作品)
キーワードThreadEventCriticalSectionSynChronize
デルファイのスレッドクラス
ラプターズ[メンタルストゥディオ]
http://mental.mentsu.com
二
最初はコンストラクターです。
constructortthread.create(createSuspended:boolean);
始める
継承料;
addthread;
fsuspended:= createSuspended;
fcreateSuspeded:= createSuspended;
fhandle:= beginthread(nil、0、@threadproc、pointer(self)、create_suspended、fthreadid);
iffhandle = 0then
raiseethread.createresfmt(@sthreadcreateerror、[syserrormessage(getlasterror)));
終わり;
このコンストラクターには多くのコードがありませんが、スレッドがここで作成されるため、最も重要なメンバーと見なすことができます。
Tobject.createを介してtobject.createを呼び出した後、最初の文はプロセスを呼び出すことです。AddThread、そのソースコードは次のとおりです。
procedureaddthread;
始める
interlockedincrement(threadCount);
終わり;
対応するremovethreadもあります。
procedureremovethread;
始める
InterlockedDecrement(threadCount);
終わり;
それらの機能は非常に単純です。これは、グローバル変数を追加または減少させることにより、プロセス内のスレッドの数をカウントすることです。ただし、ここで使用される一般的に使用されるINC/DECプロセスは、一般的に使用されるINC/DECプロセスではありませんが、InterlockedIncrement/InterlockedDecrementプロセスは使用されています。しかし、それらの間には1つの最大の違いがあります。つまり、インターロックインクリメント/インターロックデッメントはスレッドセーフです。つまり、マルチスレッドの下で実行結果が正しいことを確認できますが、Inc/Decはできません。または、オペレーティングシステム理論の観点から、これは「原始的な」操作のペアです。
2つの実装の詳細の違いを説明するために、追加を例として考えてみましょう。
一般的に言えば、メモリデータを追加する操作を分解した後、次の3つのステップがあります。
1.メモリからデータを読み取ります
2. 1つのデータを追加します
3。メモリに保存します
ここで、2スレッドアプリケーションでINCを使用して1つを追加する操作が発生する可能性があると仮定します。
1.スレッドAはメモリからデータを読み取ります(3であると仮定)
2。スレッドBはメモリからデータを読み取ります(3)
3.スレッドAはデータに追加します(現在は4です)
4.スレッドBはデータに1つ追加します(現在も4です)
5.ストアをメモリに格納します(メモリ内のデータは4になりました)
6.スレッドBはデータをメモリに保存します(メモリ内のデータはまだ4ですが、両方のスレッドがそれを追加します。これは5であるため、エラー結果がここに表示されます)
いわゆる「プリミティブ」は途切れやすい操作であるため、「プリミティブ」が実行される前にスレッドスイッチングが実行されないようにすることができるため、インターロックインクレメントプロセスに問題はありません。したがって、上記の例では、スレッドAがメモリにデータの保存を完了した後にのみ、スレッドBがそこから数値を取得し始め、1つの操作を追加できます。
前の例は、「スレッドアクセス競合」の状況も示しています。これが、これについて「同期」を必要とする理由です。
同期といえば、気晴らしがあります。カナダのウォータールー大学の教授であるLi Mingは、かつて「スレッドの同期」で「同期」として同期することに異議を唱えました非常に合理的です。中国語では、「同期」とは「同時に発生する」ことを意味し、「スレッドの同期」は「同時に発生する」ことを避けることを目的としています。英語では、同期には2つの意味があります。1つは伝統的な意味での同期(Tooccuratthesametime)、もう1つは「調整」です。 「スレッドの同期」で同期するという言葉は、後者の意味、つまり、「複数のスレッドが調整を維持し、同じデータにアクセスするときにエラーを回避することを確認する」ことを指す必要があります。ただし、IT業界では正確に翻訳されていないこのような単語がまだあります。それは明確にする必要があります。
私は遠くまで行きます、そして、私はtthreadのコンストラクターに戻ります。
fhandle:= beginthread(nil、0、@threadproc、pointer(self)、create_suspended、fthreadid);
ここでは、上記のDelphirtl関数BeginThreadを使用します。重要なパラメーターは3番目と4番目のパラメーターです。 3番目のパラメーターは、上記のスレッド関数、つまりスレッドで実行されるコードパーツです。 4番目のパラメーターは、スレッド関数に渡されるパラメーターです。ここに作成されたスレッドオブジェクト(つまり、自己)があります。他のパラメーターの中でも、5番目は、作成後にスレッドを吊り下げてすぐに実行しないように設定するために使用されます(スレッドの開始作業は、suteSspededフラグに基づいて、張り付け後の建設で決定されます)、6番目はスレッドIDを返すことです。
次に、tthreadのコア:スレッド関数threadprocを見てみましょう。興味深いことに、このスレッドクラスのコアはスレッドのメンバーではなく、グローバル関数です(beginthreadプロセスのパラメーター慣習はグローバル関数のみを使用できるためです)。これがそのコードです:
functionthreadproc(thread:tthread):integer;
var
freethread:boolean;
始める
試す
ifnotthread.terminatedthen
試す
thread.execute;
を除外する
thread.ffatalException:= acchireexceptionObject;
終わり;
ついに
freethread:= thread.ffreeonterminate;
結果:= thread.freturnValue;
thread.doterminate;
thread.ffinished:= true;
SignalSynceVent;
iffreeThreadthenthread.free;
EndThread(result);
終わり;
終わり;
コードはあまりありませんが、このコードは実際にスレッドで実行されるコードであるため、TThread全体の最も重要な部分です。以下は、コードの行ごとの説明です。
最初に、スレッドクラスの終了フラグを判断します。スレッドクラスの実行方法は、TheRead Codeを実行するために呼び出されます。基本的に派生クラスで実行コードを実行しています。
したがって、実行は、アクセス違反の防止など、スレッドクラスのスレッド関数です。
Executeで例外が発生した場合、例外オブジェクトはAcquireExceptionObjectを介して取得され、スレッドクラスのffatalExceptionメンバーに保存されます。
最後に、スレッドが終了する前にいくつかの仕上げ作業が行われます。ローカル変数FreeThreadは、スレッドクラスのフリーオンスリオンプロパティの設定を記録し、スレッドリターン値をスレッドクラスのreturn値属性の値に設定します。次に、スレッドクラスのDoterminateメソッドを実行します。
Doterminateメソッドのコードは次のとおりです。
proceduretthread.doterminate;
始める
iFassigned(fonterminate)thensynchronize(callonterminate);
終わり;
それは非常に簡単です。それは同期してcallonterminateメソッドを呼び出すことであり、callonterminateメソッドのコードは次のとおりです。
proceduretthread.callonterminate;
始める
ifassigned(fonterminate)thenfonterminate(self);
終わり;
Onterminateイベントは同期で実行されるため、本質的にスレッドコードではなく、メインスレッドコードです(後で同期する分析を参照)。
Onterminateを実行した後、スレッドクラスのファイニングされたフラグをTrueに設定します。
次に、SignalSynceVentプロセスを実行すると、そのコードは次のとおりです。
procedureSignalsyncevent;
始める
Setevent(Syncevent);
終わり;
また、非常に簡単です。グローバルなイベントをセットアップすることです。Synceventは、後で詳細に説明され、Synceventの目的はWaitforになります