2016-11-18 13 views
-1

フォーム上のリストボックスに項目を追加し、最後の項目までスクロールしてから更新したいと思います。私はこれを並列ForEachループの内部でやりたいと思います。
これを行うには、私はオンラインで拡張メソッドを見つけ、それを私のニーズに変更しました。今すぐエラーメッセージが表示されます: "クロススレッド操作が有効ではありません:コントロール 'listBox1'が作成されたスレッド以外のスレッドからアクセスされました。"私は、作業中のスレッドがListBoxにアクセスしようとしているというエラーを理解しています。実際には、エラーを受け取る前にメインスレッドがListBoxを更新できることがわかります。また、デバッガはエラーが "int visibleItems ..."という行にあることを伝えます
どうすればいいですか?ListBoxスレッドセーフ(拡張メソッド経由)に追加する方法

public static class MyClass 
{ 
    public static void AddItemThreadSafe(this System.Windows.Forms.ListBox lb, object item) 
    { 
     int visibleItems = lb.ClientSize.Height/lb.ItemHeight; 
     if (lb.InvokeRequired) 
     { 
      lb.Invoke(new MethodInvoker(delegate 
      { 
       lb.Items.Add(item); 
       lb.TopIndex = Math.Max(lb.Items.Count - visibleItems + 1, 0); 
       lb.Refresh(); 
      })); 
     } 
     else 
     { 
      lb.Items.Add(item); 
      lb.TopIndex = Math.Max(lb.Items.Count - visibleItems + 1, 0); 
      lb.Refresh(); 
     } 
    } 
} 
+1

宣言してみてくださいデリゲートブロック内の変数 "visibleItems"とelseクロック内にあります。はい、コードの重複は、動作するはずです – Pyfhon

+0

[AddItemThreadSafe'関数の上に[MethodImpl(MethodImplOptions.Synchronized)]を試してください –

+0

[MethodImpl(MethodImplOptions.Synchronized)]を試しましたが、問題は解決しません。デリゲートブロック内で "visibleItems"も移動しましたが、その変更によってプログラムがフリーズします。 – Manngo

答えて

-1

あなたはint visibleItems = lb.ClientSize.Height/lb.ItemHeight;

は、あなたが完全にvisibleItemsのprescind場合はライン上の例外が発生UIスレッド以外のスレッドを通じてClientSizeプロパティにアクセスしている、あなたは例外を取り除くが、それを取得することができますまだスレッドセーフコードではありません。あなたが必要なもの

public static void AddItemThreadSafe(this System.Windows.Forms.ListBox lb, object item) 
{ 
    if (lb.InvokeRequired) 
    { 
     lb.Invoke(new MethodInvoker(delegate 
     { 
      lb.Items.Add(item); 
      lb.TopIndex = Math.Max(lb.Items.Count - lb.ClientSize.Height/lb.ItemHeight + 1, 0); 
      lb.Refresh(); 
     })); 
    } 
    else 
    { 
     lb.Items.Add(item); 
     lb.TopIndex = Math.Max(lb.Items.Count - lb.ClientSize.Height/lb.ItemHeight + 1, 0); 
     lb.Refresh(); 
    } 
} 

は、項目の追加を行うことで、リフレッシュ、アトミック操作が(スケジューラが与えることを決定した場合、実行が一時停止することはできません。それは別のものに行く呼び出し)。あなたは、コードを実行した場合、フォームがフリーズするよう

Parallel.For(0, 1000, (x) => 
{ 
    listBox1.AddItemThreadSafe(x); 
}); 

としてParallel.Forと、

private static readonly Object obj = new Object(); 

public static void AddItemThreadSafe(this System.Windows.Forms.ListBox lb, object item) 
{ 
    if (lb.InvokeRequired) 
    { 
     lb.Invoke(new MethodInvoker(delegate 
     { 
      lock (obj) 
      { 
       // thread unsafe code 
       lb.Items.Add(item); 
       lb.TopIndex = Math.Max(lb.Items.Count - lb.ClientSize.Height/lb.ItemHeight + 1, 0); 
      }   
     })); 
    } 
    else 
    { 
     lock (obj) 
     { 
      // thread unsafe code 
      lb.Items.Add(item); 
      lb.TopIndex = Math.Max(lb.Items.Count - lb.ClientSize.Height/lb.ItemHeight + 1, 0); 
     } 
    } 
} 

しかし: あなたはそのためにロックを使用することができます。

Parallel.For(0, 1000, (x) => 
{ 
    listBox1.AddItemThreadSafe(x); 
    Application.DoEvents(); 
}); 

そしてそれはここのように、フォームを正しくレンダリングされます:あなたはそれを変更することができますので、UIは、これまでのすべてをレンダリングすることができていることを確認する必要があり

The form rendering the items in parallel

+0

これはちょうどひどい*アドバイスです。すべてのスレッドをUIスレッドにマーシャリングするのに時間を費やすようにするためには、たくさんのスレッドをスピンアップすることは、生産的な利益がないために時間が無駄になります。このように 'DoEvents'を使うべきではありません。これはメソッドの適切な使い方ではなく、このアドバイスの後に続く誰のためにも傷つく世界を引き起こすだけです。 – Servy

関連する問題