C#定时器和同步原语

时间:2017-06-09 13:15:39

标签: c# multithreading locking

我有一个简单的代码:

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

此代码不起作用:有时foreach语句抛出System.InvalidOperationException:Collection已被修改。这意味着lock语句不起作用。我尝试用Semaphore对象替换它没有任何效果。

如何确保不同的代码段一次不会修改list?为什么lockSemaphore在此代码中不起作用?

UPD: 我没有看到如此明显的错误,抱歉。我认为异常是由foreach时插入引起的,并且没有注意到list.Remove(number);。谢谢你的回答!

3 个答案:

答案 0 :(得分:3)

该例外与线程或锁定无关。这是因为您正在foreach循环中修改集合。不要那样做。而是使用简单的for循环。

类似的东西:

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

答案 1 :(得分:3)

迭代时无法修改集合。

所以,一个非常简单的单行班轮可以做到你想要的,是:

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

答案 2 :(得分:2)

正如所说,问题是你在枚举列表时调用list.Remove()。修复此问题而不修改多少代码的半简单方法是在枚举列表时创建列表的副本:

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