2016-11-18 6 views
-1

Ich möchte ein Element zu einer Listbox in einem Formular hinzufügen, blättern Sie es bis zum letzten Eintrag und aktualisieren Sie es dann. Ich würde das gerne in einer parallelen ForEach-Schleife machen.
Dazu habe ich online eine Erweiterungsmethode gefunden und nach meinen Bedürfnissen geändert. Jetzt erhalte ich die Fehlermeldung: "Cross-Thread-Operation nicht gültig: Control 'listBox1' zugegriffen von einem anderen Thread als der Thread, auf dem es erstellt wurde.". Ich verstehe den Fehler, dass ein funktionierender Thread versucht, auf die ListBox zuzugreifen. In der Tat kann ich sehen, dass der Haupt-Thread die ListBox vor dem Empfang des Fehlers aktualisieren kann. Auch der Debugger sagt mir, dass der Fehler in der Zeile "int visibleItems ..." steht.
Wie kann das gemacht werden?So fügen Sie zu ListBox thread safe (über Erweiterungsmethode) hinzu

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

versuchen, eine erklären Variable "visibleItems" in einem Delegate-Block und innerhalb der anderen Uhr. ja, Code-Duplizierung, aber sollte funktionieren – Pyfhon

+0

versuchen [MethodImpl (MethodImplOptions.Synchronized)] über die Funktion 'AddItemThreadSafe' –

+0

Ich versuchte [MethodImpl (MethodImplOptions.Synchronized)], aber es löst das Problem nicht. Ich habe auch "visibleItems" in den Delegate-Block verschoben, aber diese Änderung führt dazu, dass das Programm einfriert. – Manngo

Antwort

-1

Sie die Clientsize-Eigenschaften durch einen anderen Thread als dem UI-Thread Zugriff, die die Ausnahme auf der Leitung verursacht int visibleItems = lb.ClientSize.Height/lb.ItemHeight;

Wenn Sie visibleItems prescind vollständig, können Sie die Ausnahme loszuwerden, aber es noch wäre nicht sicher Code Thread:

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(); 
    } 
} 

Was Sie brauchen, ist das Hinzufügen von Elementen zu machen, und die Wiederholrate, eine atomare Operation (nicht erlauben, die die Ausführung zu unterbrechen, wenn der Planer zu geben, entscheidet es geht zu einem anderen aufrufen). Sie können eine Sperre für das verwenden:

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); 
     } 
    } 
} 

Aber, wenn Sie den Code ausführen, mit einem Parallel.For wie

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

Das Formular wird einfrieren. Sie müssen sicherstellen, dass die UI alles machen kann, so weit, so können Sie es ändern:

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

Und es wird das Formular korrekt, wie hier machen:

The form rendering the items in parallel

+0

Das ist einfach * schrecklich * Rat. Wenn du Tonnen von Threads spinnst, nur damit sie alle ihre ganze Zeit damit verbringen, direkt zum UI-Thread zurück zu marshallen, wirst du * Tonnen * an Zeit verschwenden, ohne produktive Gewinne zu erzielen. Sie sollten 'DoEvents' auch nicht so verwenden. Dies ist keine angemessene Anwendung der Methode und wird nur für jemanden, der einem dieser Ratschläge folgt, zu einer Verletzung führen. – Servy

Verwandte Themen