2017-06-09 1 views
0

Ich habe einen einfachen Code:C# Timer und synchrononization Primitiven

public partial class Form1 : Form 
{ 
    Random rng = new Random(); 
    List<int> list = new List<int>(); 
    static object lockObject = new object(); 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void timer1_Tick(object sender, EventArgs e) 
    { 
     lock (lockObject) 
     { 
      foreach (int number in list) //Exception appears here 
       if (number > 10) 
        list.Remove(number); 
     } 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     lock (lockObject) 
     { 
      list.Add(rng.Next(20)); 
     } 
    } 
} 

Dieser Code funktioniert nicht: manchmal foreach Aussage wirft System.InvalidOperationException: Die Auflistung wurde geändert. Das bedeutet, dass lock Anweisung nicht funktioniert. Ich habe versucht, es mit Semaphore Objekt ohne Wirkung zu ersetzen.

Wie kann ich sicherstellen, dass verschiedene Codeabschnitte list nicht gleichzeitig ändern? Warum funktioniert lock und Semaphore nicht in diesem Code?

UPD: Ich habe nicht so offensichtlichen Fehler gesehen, sorry. Ich dachte, die Ausnahme wurde durch Einfügung verursacht, während foreach, und habe nicht bemerkt list.Remove(number);. Danke für deine Antworten!

+4

Sie können eine Sammlung nicht ändern, während über sie iterieren. – dcg

+0

@ dcg Ich weiß. Ich möchte wissen, wie sichergestellt werden kann, dass die Sammlung nicht geändert wird, während ich darüber iteriere. – AndrewR

+0

@AndrewR Sie sichern es, indem Sie es nicht tun? – FakeCaleb

Antwort

3

Sie nicht eine Sammlung während Iteration über sie ändern können.

So eine sehr einfache Einzeiler zu tun, was Sie wollen, wäre:

list.RemoveAll(x => x > 10); 
3

Die Ausnahme hat nichts mit Threads oder Sperren zu tun. Dies liegt daran, dass Sie die Sammlung in einer foreach-Schleife ändern. TU das nicht. Verwenden Sie stattdessen eine einfache for Schleife.

Etwas wie:

for (int i = list.Count - 1; i > -1; i--) 
{ 
    if (number > 10) 
    { 
     if (list[i] == number) 
     { 
      list.RemoveAt(i); 
     } 
    } 
} 
+0

Die Iteration sollte vom letzten bis zum ersten Punkt durchgeführt werden, damit Sie keine Probleme mit Indizes bekommen. – dcg

+2

Ich denke, Sie müssen i--, nicht i ++;) – Compufreak

+0

Ich glaube, die Bedingung innerhalb der for ist 'if (list [i]> 10) {list.RemoveAt (i);}', so der äußere 'if Um es herum muss entfernt werden. – dcg

2

wie gesagt, das Problem ist, die Sie anrufen list.Remove(), während die Liste aufzählt. ein halb-einfache Möglichkeit, dies zu beheben, ohne viel Code zu ändern, ist eine Kopie der Liste zu erstellen, wenn Sie es aufzählen mag:

foreach (int number in new List<int>(list)) 
{ 
    if (number > 10) 
     list.Remove(number); 
}