2009-10-12 68 views
0

我是一个开始编程。线程锁澄清

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

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

    } 
} 

那怕我reciving,“InvalidOperationException异常”。什么是代码的改变?

回答

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

您正在枚举集合foreach (string str in list),同时在AddItems()中对其进行修改。对于这个代码工作财产你或者必须Thread.Join()这两个线程(以便两个将完成项目添加到列表;我不知道,但如果Add是线程安全;我敢打赌它不是,所以你将不得不通过锁定SyncRoot)或使用ReaderWriterLock来逻辑分隔这些操作。

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的解决方案)来了解两个线程何时完成。

+0

为什么-1?他正试图理解这个问题,这有助于解释它。 – 2009-10-12 15:03:56

+0

+1约约费希尔 – 2009-10-12 15:13:56

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

约3分钟迟到。 ;)这几乎是我的一个重复。 – 2009-10-12 15:05:11

+0

是的,自从你第一次以来,我已经升级了你的答案。 (当我还在vs.net上运行示例时,您必须发布它)。 – 2009-10-12 15:06:54

0

我改变了代码,这证明锁没有做任何事情。 我预计add2不会显示,直到add1完成。但add1和add2只是混合在一起。使用系统的 ;使用System.Threading的 ;

公共静态类实例 {

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

     } 
    } 
} 

}