线程锁定澄清

时间:2009-10-12 14:56:53

标签: c# multithreading

我是编程的初学者。

当我通过锁定操作执行代码时:

class ThreadSafe
{
    static List<string> list = new List<string>();
    static object obj=new object();
    static void Main()
    {
        new Thread(AddItems).Start();
        new Thread(AddItems).Start();

        foreach (string str in list)
        {
            Console.WriteLine(str);
        }
        Console.WriteLine("Count=" + list.Count.ToString());
        Console.ReadKey(true);
    }
    static void AddItems()
    {
        lock (obj)
        {
            for (int i = 1; i < 10; i++)
             list.Add("Item " + i.ToString());
        }

    }
}

即使我收到“InvalidOperationException”。代码更改会是什么?

5 个答案:

答案 0 :(得分:3)

问题是你的线程在尝试读取时会改变列表。

class ThreadSafe
{
    static List<string> list = new List<string>();
    static object obj=new object();
    static void Main()
    {
        var t1 = new Thread(AddItems);
        var t2 = new Thread(AddItems);

        t1.Start();
        t2.Start();

        t1.Join();
        t2.Join();

        foreach (string str in list)
        {
            Console.WriteLine(str);
        }
        Console.WriteLine("Count=" + list.Count.ToString());
        Console.ReadKey(true);
    }
    static void AddItems()
    {
        for (int i = 1; i < 10; i++)
            lock (obj)
            {
                list.Add("Item " + i.ToString());
            }
    }
}

不同之处在于此代码在显示结果之前等待两个线程完成。

我还围绕需要锁定的特定指令移动了锁,以便两个线程可以同时运行。

答案 1 :(得分:1)

您正在使用foreach (string str in list)枚举一个集合,同时在AddItems()中对其进行修改。要使这个代码工作属性,你要么必须Thread.Join()两个线程(这样两者都会完成向列表中添加项目;我不确定,但是如果Add是线程安全的话;我打赌它是不是,所以你必须通过锁定SyncRoot)或使用ReaderWriterLock来逻辑地区分这些操作来解释这一点。

答案 2 :(得分:1)

在两个AddItems线程完成填充列表之前,您正在循环遍历结果列表。因此,foreach抱怨列表在循环遍历该列表时已更新。

这样的事情应该会有所帮助:

System.Threading.Thread.Sleep(0); // Let the other threads get started on the list.
lock(obj) 
{
  foreach (string str in list)
  {
    Console.WriteLine(str);
  }
}

注意!这并不能保证第二个线程在读完第一个线程提供的列表之前完成它的工作(假设第一个线程首先获取锁定)。

在阅读结果之前,您需要一些其他机制(如John Gietzen的解决方案)来了解两个线程何时完成。

答案 3 :(得分:1)

使用调试器。 :)

您在foreach上收到InvalidOperationException。 会发生什么,是在线程仍在运行时执行foreach。 因此,当您将项目添加到列表中时,您将遍历列表。因此,列表的内容正在发生变化,因此,foreach会抛出异常。

您可以通过调用“加入”来避免此问题。

        static void Main()
        {
            Thread t1 = new Thread (AddItems);
            Thread t2 =new Thread (AddItems);

            t1.Start (); 
            t2.Start ();

            t1.Join ();
            t2.Join ();

            foreach( string str in list )
            {
                Console.WriteLine (str);
            }
            Console.WriteLine ("Count=" + list.Count.ToString ());
            Console.ReadKey (true);
        }

答案 4 :(得分:0)

我已经更改了代码,这证明锁没有任何作用。 我希望add2在add1完成之前不会出现。但add1和add2混合在一起。 使用系统; 使用System.Threading;

public static class Example {

public static void Main()
{
    int data= 0;

    Thread t1 = new Thread(()=> add1(ref data));
    Thread t2 = new Thread(() => add2(ref data));
    t1.Start();
    t2.Start();
}

static void add1(ref int x)
{
    object lockthis = new object();
    lock (lockthis)
    {
        for (int i = 0; i < 30; i++)
        {
            x += 1;
            Console.WriteLine("add1 " + x);

        }
    }
}
static void add2(ref int x)
{
    object lockthis = new object();
    lock (lockthis)
    {
        for (int i = 0; i < 30; i++)
        {
            x += 3;
            Console.WriteLine("add2 " + x);

        }
    }
}

}