1. ホーム
  2. c#

[解決済み】クロススレッド操作が有効でない。作成されたスレッド以外のスレッドからアクセスされたコントロール

2022-02-03 14:23:33

質問事項

あるシナリオがあります。(Windows Forms, C#, .NET)

  1. あるユーザーコントロールをホストするメインフォームがあります。
  2. このユーザーコントロールは重いデータ操作を行うので、もし私が直接 UserControl_Load メソッドを実行する間、UI は無応答になります。
  3. これを克服するために、私は別のスレッドでデータをロードします(できるだけ既存のコードを変更しないようにしています)。
  4. 私はバックグラウンドワーカスレッドを使いました。このスレッドはデータの読み込みを行い、完了したらアプリケーションにその作業を完了したことを通知します。
  5. さて、ここからが本当の問題です。すべてのUI(メインフォームとその子ユーザーコントロール)は、プライマリメインスレッドで作成されました。usercontrolのLOADメソッドでは、userControl上のいくつかのコントロール(テキストボックスなど)の値に基づいてデータを取得しています。

疑似コードは次のようになります。

コード1

UserContrl1_LoadDataMethod()
{
    if (textbox1.text == "MyName") // This gives exception
    {
        //Load data corresponding to "MyName".
        //Populate a globale variable List<string> which will be binded to grid at some later stage.
    }
}

Exceptionは

クロススレッド操作が有効ではありません。作成されたスレッド以外のスレッドからコントロールにアクセスした。

このことについてもっと知るために、私はグーグルで検索し、次のコードを使用するような提案を得ました。

コード 2

UserContrl1_LoadDataMethod()
{
    if (InvokeRequired) // Line #1
    {
        this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
        return;
    }

    if (textbox1.text == "MyName") // Now it wont give an exception
    {
    //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be binded to grid at some later stage
    }
}

しかし、しかし、しかし・・・、どうやら振り出しに戻ったようだ。アプリケーションは再び 無応答になる。1行目のif条件の実行が原因のようです。ロードタスクは、私がスポーンした3番目のスレッドではなく、再び親スレッドによって行われます。

私の認識が正しいのか間違っているのかわかりません。私はスレッディングの初心者なのです。

また、Line#1 if ブロックの実行はどのように影響するのでしょうか。

状況は次のとおりです。 : コントロールの値に基づいて、グローバル変数にデータをロードしたい。子スレッドからコントロールの値を変更したくありません。子スレッドからは絶対にやらない。

つまり、対応するデータをデータベースから取得できるように、値にのみアクセスするのです。

どのように解決するのですか?

の通りです。 Prerak Kの更新コメント (削除済み)。

<ブロッククオート

質問の仕方が悪かったようです。

状況はこうです。コントロールの値に基づいて、グローバル変数にデータをロードしたい。子スレッドからコントロールの値を変更したくありません。子スレッドからは絶対にやらない。

つまり、対応するデータをデータベースから取得できるように、値にのみアクセスするのです。

そのとき、あなたが望む解決策は、次のようになるはずです。

UserContrl1_LOadDataMethod()
{
    string name = "";
    if(textbox1.InvokeRequired)
    {
        textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
    }
    if(name == "MyName")
    {
        // do whatever
    }
}

本格的な処理は別スレッドで行う 以前 コントロールのスレッドに戻ろうとしたとき。例えば

UserContrl1_LOadDataMethod()
{
    if(textbox1.text=="MyName") //<<======Now it wont give exception**
    {
        //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be
        //bound to grid at some later stage
        if(InvokeRequired)
        {
            // after we've done all the processing, 
            this.Invoke(new MethodInvoker(delegate {
                // load the control with the appropriate data
            }));
            return;
        }
    }
}