如何正确锁定对象

时间:2011-03-29 11:37:52

标签: c# .net multithreading

我刚刚阅读了一篇关于线程的精彩教程,并且遇到了锁问题。我需要一些提示/建议,指出我正确的方向。我想了解为什么输出没有像我期望的那样排序。代码显示了我的简单示例。

  class Program {
    class A {
      public object obj = new object();
      public int i;
    }
    class B {
      public object obj = new object();
      public int j;
    }
    static void Main() {
      Console.Write("Thread1: ");
      A a = new A();
      for (a.i = 0; a.i < 9; a.i++) {
        lock (a) {
          new Thread(() => { Console.Write(a.i); }).Start();
        }
      }
      Thread.Sleep(500);
      Console.Write("\nThread2: ");
      B b = new B();
      for (b.j = 0; b.j < 9; b.j++) {
        new Thread(() => { lock (b) { Console.Write(b.j); } }).Start();
      }
      Console.ReadLine();
    }
  }

Example output:
Thread1: 222456799
Thread2: 233357889

Link to the tutorial:
http://www.albahari.com/threading/

3 个答案:

答案 0 :(得分:2)

您只是在创建线程时锁定,或者(在第二种情况下)访问该值。锁必须由所有线程使用,否则它们什么也不做。这是试图获得阻止的锁的行为。即使您确实锁定了两个线程,也无法帮助您在特定时间点将每个线程与a.i(等)的值结合(不再存在)。

同样,线程按照自己的节奏工作;除非你有一个工人和队列,否则你不能保证订单;或者你实施自己的重新订购。

它将以自己的速度运行,并且由于您正在捕获变量 a,因此字段a.i完全可能在线程发生变化得到Console.Write。相反,您应该通过复制来捕获

  A a = new A();
  for (a.i = 0; a.i < 9; a.i++) {
    var tmp = a.i;
    new Thread(() => { Console.Write(tmp); }).Start();
  }

(或者可能完全删除a

  for (int i = 0; i < 9; i++) {
    var tmp = i;
    new Thread(() => { Console.Write(tmp); }).Start();
  }

答案 1 :(得分:1)

这里有几个问题:

首先,当你创建一个线程时,你就锁定了一个线程,所以创建了线程,但是你的原始主线程然后释放锁并继续在循环中进行卡车运输,同时创建线程同时运行。

您希望将第一个锁移动到使用A到Thread委托的线程中,如下所示:

for(a.i=0;a.i<9;a.i++)
{
  int id=a.i;
  new Thread(()=>{ lock(a){Console.Out.WriteLine("Thread{0} sees{1}",id,a.i)};}).Start(); // lots of smileys here :)
}

如果你仔细观察,你会注意到A和B没有以相同的方式锁定线程,这告诉你线程过着自己的生活和线程创建!=线程生命。

即使锁定了您的线程运行程序,您也可以在线程1在线程2之后运行的情况下结束...但由于您的锁定它们将永远不会同时运行。

您还在所有线程中引用共享成员:a.i。该成员在主线程中初始化,不会锁定任何内容,因此您的行为是不可预测的。这就是为什么我添加捕获的变量i,它在创建线程时抓取a.i的值,并以安全的方式在线程委托中使用。

此外,始终锁定非公共实例。如果您锁定A,请确保没有人看到A并有机会锁定它。

答案 2 :(得分:1)

因为锁始终由主线程保持,因为您在获取锁定后启动线程并且一旦获得就没有争用。现在线程可以自由运行,但是主线程启动的线程不会被任何锁同步。接近你的预期的东西是跟随(仅订单)再次计数取决于你有多快和多少核心。观察b.j ++现在在锁内。

    for (b.j = 0; b.j < 9; )
    {
        new Thread(() => { lock (b) { Console.Write(b.j); b.j++; } }).Start();
    }

锁定或关键部分背后的基本思想是只允许一件事发生,而不是顺序,在上面的修改中我已经锁定了增量操作,那就是在下一个线程开始运行代码锁定之前,当前线程有在释放锁之前完成在其获取的锁下运行所有​​代码。