2012-01-04 15 views
1

ボタンのTextBackgroundWorkerから変更しています。私は例外がスローされたと思った。それはなぜですか?BackgroundWorkerがUIコンポーネントを変更するのはなぜですか?

なぜCross-thread operation not valid: ... accessed from a thread other than the thread it was created on.が届かないのですか?

編集:ありがとうございました。

おそらく理由は、UIにThread.Sleep(1000);があるということでした。

public Form1() 
{ 
    InitializeComponent(); 

    backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); 
    backgroundWorker1.RunWorkerAsync(); 
    Thread.Sleep(1000); 
} 

void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    button1.Text = "a"; 
} 

しかし、この次のコードは、UIに間接的に影響を与えてもうまく動作することに気付きました。

public partial class Form1 : Form 
{ 
    int i; 

    public Form1() 
    { 
     InitializeComponent(); 

     i = 1; 
     backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); 
     backgroundWorker1.RunWorkerAsync(); 
     for (int j = 0; j < 100000000; j++) ; 
     button1.Text = i.ToString(); 
    } 

    void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     i = 2; 
    } 
} 

なぜですか?

+0

ソースコードを表示してください... – Yahia

+0

これはなに? 'for(int j = 0; j <100000000; j ++); (int j = 0; j <100000000; j ++){button1.Text = i.ToString();} } '? – user973511

+0

@Fuex No.このループは、UIスレッドが実行されている間にBGWによって 'i'が変更されていることを証明することです。その 'Text'は' 2'に終わります。 – ispiro

答えて

0

答えはおそらくクロススレッド操作実行されます。彼らはちょうどトラブルになりやすいです。そしてControl(これは例外を投げることになっています)は、Sleepであれば、おそらくスレッドを横切ることを気にしません。

5

作業が完了すると、バックグラウンドワーカーによって呼び出されるコールバック関数(BackgroundWorker.RunWorkerCompleted)は、UIスレッドディスパッチャーを既に使用しています。

編集:

@ispiro:それはあなたの第二の例のコードがします常に作業することを保証していない - あなたはまだあなたがそれをするvolatileを宣言すべき変数iのクロススレッドの更新を持っています常に正しく更新されていることを確認してください。

最初のコードは、であってはならない理由は、.NETフレームワークがこのクロススレッドアクセスを検出するのに役立つからです。 @ Greg Dが指摘したように、これは無効にすることができます(これは間違いなくnoです)。詳細については、このMSDN pageをチェックしてください。

の.NET Frameworkを使用すると、スレッドセーフではない方法であなたの コントロールにアクセスしているときに検出することができます。 アプリケーションをデバッガで実行していて、コントロールを作成した 以外のスレッドがそのコントロールを呼び出そうとした場合、デバッガ は、 "制御コントロール 以外のスレッドからアクセスされた名前のInvalidOperationExceptionを発生させます。それが作成されたスレッドです。

この例外は、デバッグ中に確実に発生し、実行時に の状況で発生します。 .NET Frameworkで書き込んだアプリケーションが .NET Frameworkバージョン2.0より前の場合、この例外が表示されることがあります。この の問題が発生したときに修正することを強くお勧めしますが、 CheckForIllegalCrossThreadCallsプロパティーをfalseに設定することで、この問題を無効にすることができます。これにより、 コントロールがVisual Studio .NET 2003および .NET Framework 1.1で実行されるように実行されます。

+0

ありがとうございます。 (私は質問を更新しました) – ispiro

+0

"最初のコードがうまくいかない理由は..." - それは本質的に私の質問です。それは_does_です。答えは、 'Sleep'がスレッドの競合を解決するのに役立つと仮定します。私はまだ理解していない、しかし、なぜ2番目の作品が動作します。 (VSはどちらにも例外を投げていません)。 – ispiro

1

便宜上、RunWorkerCompletedイベントのコールバック関数がUIスレッドで呼び出される(呼び出される)ためです。これはの場合はすべてイベントでは当てはまりませんが、明らかにDoWorkコールバックは別のスレッドで実行されます。

+0

ありがとうございます。 (私は質問を更新しました。) – ispiro

1

いくつかの可能性があります。

1)プログラムでクロススレッドチェックが無効になっています。これは、人々がUIスレッドのスレッドルールを理解していないときにソフトウェアが使用する、悲しいことに一般的なハックです。

2)あなたのプログラムは、BackgroundWorkerのProgressまたはCompletedイベントでUIを変更しています。これらのイベントは、BackgroundWorkerを作成したスレッドの同期コンテキストにマーシャリングされます。 BackgroundWorkerが古典的なコンテキストでWinFormsデザイナーコンポーネントとして使用されている場合、あなたは金色です。イベントはUIスレッドにマーシャリングされているので、あなたは自分自身でそのようなナンセンスを行う必要はありません。

+0

ありがとうございました。 (私は質問を更新しました) – ispiro

1

この質問は、BackgroundWorderの特定のの保証を提供していますので、誤解を招くようなものです。

をあなたがいないに注意する必要がありますDoWorkイベントハンドラに任意のユーザインターフェイスオブジェクトを操作し、[ありません]:次はBGWのドキュメントから「注意」です。代わりに、ProgressChangedイベントとRunWorkerCompletedイベントを介してユーザーインターフェイスと通信します。

RunWorkerCompletedProgressChangedイベントはBGW *を作成したスレッドに投稿です。同じ保証しかし、DoWorkイベントにを保持していない:からUIにアクセスしないこと。本質的に :)

RunWorkerCompletedProgressChangedでコードが効果的に自動的にControl.BeginInvoke、「ポストに包まれている内ウィンドウ/スレッドのディスパッチ・キューに「コールバックを呼び出すメッセージ」を送信します。

ハッピーコーディング。


* を作成したスレッドので、(または多分それはを開始しだ?)コールバックが掲載されますBGW効果、その希望の「実行」しませんBGWを作成することが可能ですUIスレッド。このような奇妙な動作を避けるため、は常にポストバックするUIスレッドでBGWを作成/開始します。

+2

実際には、一般的に同じスレッドです。しかし、C++ - yの技術を得ているのであれば、それはスレッドの同期コンテキストです。理論的には同期コンテキストはどこにでもポストすることができます。実用的なアプリケーションが通常は呼び出しスレッドにポストします。 :) –

+0

@GregDこれは個人的な経験でした:)私は間違って前に別のスレッドでBGWを作成しました。楽しい時間。 –

+0

ありがとうございます。 (私は質問を更新しました。) – ispiro

関連する問題