コントロールを作成しなかったスレッドからvclを更新したり、ウィンドウにメッセージを送信したりするには、Synchronizeを呼び出す必要があることを知っています。
スレッドセーフではないという言葉をよく耳にしますが、何が起こっているのかについての実際の説明を見つけることができません。
アプリケーションがアクセス違反でクラッシュする可能性があることは知っていますが、理由はわかりません。
このトピックに光を当ててください。
コントロールを作成しなかったスレッドからvclを更新したり、ウィンドウにメッセージを送信したりするには、Synchronizeを呼び出す必要があることを知っています。
スレッドセーフではないという言葉をよく耳にしますが、何が起こっているのかについての実際の説明を見つけることができません。
アプリケーションがアクセス違反でクラッシュする可能性があることは知っていますが、理由はわかりません。
このトピックに光を当ててください。
VCL UI コントロールでスレッドが安全でない最大の原因の 1 つは、TWinControl.Handle
プロパティ ゲッターです。コントロールの の単純な読み取り専用アクセサーではありませんHWND
。HWND
また、まだ存在しない場合も作成します。Handle
プロパティがまだ存在しないときにワーカー スレッドがプロパティを読み取ると、ワーカー スレッド コンテキスト内にHWND
新しいプロパティが作成されます。コントロールはメイン メッセージ ループを通過しなくなります。さらに悪いことに、メイン スレッドがワーカー スレッドと同時に同じプロパティを読み取る場合(たとえば、メイン スレッドがプロパティを動的に再作成している場合)HWND
HWND
Handle
Handle
さまざまな理由で)、スレッド コンテキストが作成する との間に競合状態があり、HWND
それが new として割り当てられます。また、両方のスレッドが最終的に新しい を作成するが、一方しか保持できず、もう一方が保持されるHandle
場合、潜在的なハンドル リークの可能性があります。HWND
漏れます。
thread-unsafety のもう 1 つの違反者は、VCL の関数です。VCL は、ウィンドウのメッセージ プロシージャとして非静的クラス メソッドMakeObjectInstance()
を割り当てるために内部的に使用します。関数 (例で使用)。 複数のスレッドによる同時アクセスから保護されていないメモリコンテンツのかなりの量のメモリ割り当て/キャッシュおよびいじりを行います。TWinControl.WndProc()
TWinControl.Handle
TWndMethod
HWND
AllocateHWnd()
TTimer
MakeObjectInstance()
コントロールHandle
が事前に割り当てられているHandle
ことを確認でき、ワーカー スレッドの実行中にメイン スレッドがそれを再作成しないことを確認できる場合は、を使用せずにワーカー スレッドからそのコントロールにメッセージを安全に送信できますSynchronize()
。しかし、ワーカー スレッドが考慮しなければならない要因が多すぎるため、お勧めできません。そのため、すべてのUI アクセスをメイン スレッドのみで行うことが最善です。これが VCL UI システムの使用方法です。
Windows での GDI スレッド セーフについては、この参照記事を参照してください。
複数のスレッドから安全にハンドルにアクセスできることは明確に述べられていますが、同時に作成すべきではありません。クリティカル セクションなどを使用して、GDI ハンドルへのアクセスを保護する必要があります。
GDI ハンドルは、ほとんどの Windows ハンドルと同様に、 (新しい Windows では 64 ビット互換性のために) にマップされた内部構造のポインターであることに注意してください。マルチスレッド コンピューティングでは常にそうであるように、同じコンテンツに同時にアクセスすると問題が発生する可能性があり、その問題を特定して修正することは非常に困難です。integer
NativeUInt
VCL 自体の UI 部分は、スレッド セーフではない Windows API に依存していたため、最初からスレッド セーフを意図したものではありませんでした。たとえば、別のスレッドでまだ必要な GDI オブジェクトをスレッドで解放すると、潜在的な GPF に直面することになります。
Embarcadero は (現時点では) VCL をスレッドセーフにして、クリティカル セクションを介してすべての UI アクセスをシリアル化することができましたが、複雑さが増し、全体的なパフォーマンスが低下した可能性があります。Microsoft .Net プラットフォーム ( WinFormsとWPFの両方) でも、UI アクセス用の専用スレッド、AFAIK が必要であることに注意してください。
したがって、複数のスレッドから UI を更新するには、いくつかのパターンがあります。
Synchronize
スレッドからの呼び出しを 使用します。WM_USER
バックグラウンド スレッドからGDI カスタム メッセージ ( を参照) を送信して、更新が必要であることを UI スレッドに通知します。私の見解では、ほとんどの UI ではオプション 2 を使用し、リモート クライアント サーバー アクセスには追加のオプション 3 (オプション 2 と組み合わせることができます) を使用します。したがって、サーバー側から UI への更新イベントをトリガーする必要はありません。HTTP/AJAX RESTfulの世界では、これは確かに理にかなっています。オプション1はやや遅いです、IMHO。いずれの場合も、オプション 2 と 3 は、ロジックと UI が混在していない明確なn 層レイヤード アーキテクチャを想定しています。
ハンドルを持つ Windows コントロールはスレッド セーフではなく(つまり、2 つの異なるスレッドから同時に安全にアクセスすることはできません)、Delphi は Windows コントロールをラップして VCL コントロールを提供します。コントロールはメインの GUI スレッドによってアクセスされるため、別のスレッドを実行している場合はそのままにしておく必要があります。