2016-11-18 31 views
-1

我想添加一個項目到窗體上的列表框,向下滾動到最後一個條目,然後刷新它。我想在並行ForEach循環中完成此操作。
爲此,我在網上找到了一個擴展方法,並將其改爲我的需要。現在,我收到錯誤消息:「跨線程操作無效:從其創建的線程以外的線程訪問控件'listBox1'」。我明白錯誤是工作線程試圖訪問列表框。事實上,我可以看到主線程可以在接收錯誤之前更新列表框。此外調試器告訴我,錯誤是在行「int visibleItems ...」
這怎麼能做到?如何添加到列表框線程安全(通過擴展方法)

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」在代表塊內部和其他時鐘內。是的,代碼重複,但應該工作 – Pyfhon

+0

嘗試[MethodImpl(MethodImplOptions.Synchronized)]上面的函數AddItemThreadSafe' –

+0

我試過[MethodImpl(MethodImplOptions.Synchronized)]但它不能解決問題。我也在代理塊中移動了「visibleItems」,但是這種改變導致程序凍結。 – Manngo

回答

-1

您通過比UI線程,這將導致對int visibleItems = lb.ClientSize.Height/lb.ItemHeight;

如果prescind visibleItems完全線路異常其他線程訪問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(); 
    } 
} 

你需要的是做項目的增加,和刷新,原子操作(不允許執行暫停如果調度決定給它是一個去另一個調用)。 您可以使用該鎖:

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如

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

形式將凍結。你需要確保用戶界面可以使一切都那麼遠,所以你可以把它改成:正確

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

它將呈現形式,就像這裏:

The form rendering the items in parallel

+0

這只是*糟糕的*建議。爲了讓所有人花費他們所有的時間編排回UI線程,將會浪費大量的時間,而不會產生任何收益。你也不應該像這樣使用'DoEvents';這不是該方法的適當用法,只會導致任何遵循這些建議的人受到傷害。 – Servy