8

コントロールを作成しなかったスレッドからvclを更新したり、ウィンドウにメッセージを送信したりするには、Synchronizeを呼び出す必要があることを知っています。

スレッドセーフではないという言葉をよく耳にしますが、何が起こっているのかについての実際の説明を見つけることができません。

アプリケーションがアクセス違反でクラッシュする可能性があることは知っていますが、理由はわかりません。

このトピックに光を当ててください。

4

3 に答える 3

12

VCL UI コントロールでスレッドが安全でない最大の原因の 1 つは、TWinControl.Handleプロパティ ゲッターです。コントロールの の単純な読み取り専用アクセサーではありませんHWNDHWNDまた、まだ存在しない場合も作成します。Handleプロパティがまだ存在しないときにワーカー スレッドがプロパティを読み取ると、ワーカー スレッド コンテキスト内にHWND新しいプロパティが作成されます。コントロールはメイン メッセージ ループを通過しなくなります。さらに悪いことに、メイン スレッドがワーカー スレッドと同時に同じプロパティを読み取る場合(たとえば、メイン スレッドがプロパティを動的に再作成している場合)HWNDHWNDHandleHandleさまざまな理由で)、スレッド コンテキストが作成する との間に競合状態があり、HWNDそれが new として割り当てられます。また、両方のスレッドが最終的に新しい を作成するが、一方しか保持できず、もう一方が保持されるHandle場合、潜在的なハンドル リークの可能性があります。HWND漏れます。

thread-unsafety のもう 1 つの違反者は、VCL の関数です。VCL は、ウィンドウのメッセージ プロシージャとして非静的クラス メソッドMakeObjectInstance()を割り当てるために内部的に使用します。関数 (例で使用)。 複数のスレッドによる同時アクセスから保護されていないメモリコンテンツのかなりの量のメモリ割り当て/キャッシュおよびいじりを行います。TWinControl.WndProc()TWinControl.HandleTWndMethodHWNDAllocateHWnd()TTimerMakeObjectInstance()

コントロールHandleが事前に割り当てられているHandleことを確認でき、ワーカー スレッドの実行中にメイン スレッドがそれを再作成しないことを確認できる場合は、を使用せずにワーカー スレッドからそのコントロールにメッセージを安全に送信できますSynchronize()。しかし、ワーカー スレッドが考慮しなければならない要因が多すぎるため、お勧めできません。そのため、すべてのUI アクセスをメイン スレッドのみで行うことが最善です。これが VCL UI システムの使用方法です。

于 2012-05-18T15:16:44.477 に答える
8

Windows での GDI スレッド セーフについては、この参照記事を参照してください

複数のスレッドから安全にハンドルにアクセスできることは明確に述べられていますが同時に作成すべきではありません。クリティカル セクションなどを使用して、GDI ハンドルへのアクセスを保護する必要があります。

GDI ハンドルは、ほとんどの Windows ハンドルと同様に、 (新しい Windows では 64 ビット互換性のために) にマップされた内部構造のポインターであることに注意してください。マルチスレッド コンピューティングでは常にそうであるように、同じコンテンツに同時にアクセスすると問題が発生する可能性があり、その問題を特定して修正することは非常に困難です。integerNativeUInt

VCL 自体の UI 部分は、スレッド セーフではない Windows API に依存していたため、最初からスレッド セーフを意図したものではありませんでした。たとえば、別のスレッドでまだ必要な GDI オブジェクトをスレッドで解放すると、潜在的な GPF に直面することになります。

Embarcadero は (現時点では) VCL をスレッドセーフにして、クリティカル セクションを介してすべての UI アクセスをシリアル化することができましたが、複雑さが増し、全体的なパフォーマンスが低下した可能性があります。Microsoft .Net プラットフォーム ( WinFormsWPFの両方) でも、UI アクセス用の専用スレッド、AFAIK が必要であることに注意してください。

したがって、複数のスレッドから UI を更新するには、いくつかのパターンがあります。

  1. Synchronizeスレッドからの呼び出しを 使用します。
  2. WM_USERバックグラウンド スレッドからGDI カスタム メッセージ ( を参照) を送信して、更新が必要であることを UI スレッドに通知します。
  3. ステートレスなアプローチを採用する: UI はロジック レイヤーからコンテンツを時々更新します (タイマーを使用するか、データを変更する可能性のあるボタンを押したときに)。

私の見解では、ほとんどの UI ではオプション 2 を使用し、リモート クライアント サーバー アクセスには追加のオプション 3 (オプション 2 と組み合わせることができます) を使用します。したがって、サーバー側から UI への更新イベントをトリガーする必要はありません。HTTP/AJAX RESTfulの世界では、これは確かに理にかなっています。オプション1はやや遅いです、IMHO。いずれの場合も、オプション 2 と 3 は、ロジックと UI が混在していない明確なn 層レイヤード アーキテクチャを想定しています。

于 2012-05-18T12:19:12.077 に答える
4

ハンドルを持つ Windows コントロールはスレッド セーフではなく(つまり、2 つの異なるスレッドから同時に安全にアクセスすることはできません)、Delphi は Windows コントロールをラップして VCL コントロールを提供します。コントロールはメインの GUI スレッドによってアクセスされるため、別のスレッドを実行している場合はそのままにしておく必要があります。

于 2012-05-18T09:17:07.827 に答える