关于C#中的lock语句的困惑

时间:2012-03-08 16:47:54

标签: c# multithreading locking block critical-section

这是来自MSDN: lock关键字确保一个线程不会进入关键部分代码而另一个线程位于关键部分

关键部分必须与关键部分相同吗?

或者是否意味着: lock关键字确保一个线程不会进入由对象保护的任何关键部分,而另一个线程在由同一个对象保护的任何关键部分。 ?

    class Program
{
    static void Main(string[] args)
    {
        TestDifferentCriticalSections();

        Console.ReadLine();
    }

    private static void TestDifferentCriticalSections()
    {
        Test lo = new Test();

        Thread t1 = new Thread(() =>
        {
            lo.MethodA();
        });
        t1.Start();

        Thread t2 = new Thread(() =>
        {
            lo.MethodB();
        });
        t2.Start();
    }
}

public class Test
{
    private object obj = new object();

    public Test()
    { }

    public void MethodA()
    {
        lock (obj)
        {
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(500);
                Console.WriteLine("A");
            }
        }
    }

    public void MethodB()
    {
        lock (obj)
        {
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(500);
                Console.WriteLine("B");
            }
        }
    }
}

6 个答案:

答案 0 :(得分:64)

问题是措辞混乱,到目前为止的答案也不是特别清楚。让我把这个问题改成几个问题:

  

(1)lock语句是否确保一次锁定语句的主体中只有一个以上的线程?

即可。例如:

static readonly object lock1 = new object();
static readonly object lock2 = new object();
static int counter = 0;
static object M()
{
    int c = Interlocked.Increment(ref counter);
    return c % 2 == 0 ? lock1 : lock2;
}

...
lock(M()) { Critical(); }

两个线程可能同时位于lock语句的主体中,因为lock语句锁定了两个不同的对象。线程Alpha可以调用M()并获取lock1,然后线程Beta可以调用M()并获取lock2。

  

(2)假设我的lock语句总是锁定在同一个对象上,一个lock语句是否确保一次锁定主体中只有一个“活动”线程?

是。如果你有:

static readonly object lock1 = new object();
...
lock(lock1) { Critical(); }

然后线程Alpha可以获取锁定,并且线程Beta将阻止,直到锁定可用,然后才能进入锁体。

  

(3)假设我有两个锁定语句,并且每次锁定语句都锁定在同一个对象上,锁定语句是否确保在任何一个锁定的主体中只有一个“活动”线程时间?

是。如果你有:

static readonly object lock1 = new object();
...
static void X() 
{
    lock(lock1) { CriticalX(); }
}
static void Y() 
{
    lock(lock1) { CriticalY(); }
}

然后如果线程Alpha在X中并且获取锁定,并且线程Beta在Y中,则线程Beta将阻止直到锁定可用,然后才能进入锁体。

  

(4)你为什么把“活跃”放在“恐吓引号”中?

提醒用户注意等待线程可能在锁体中。您可以使用Monitor.Wait方法“暂停”锁定主体中的线程,并允许阻塞的线程变为活动状态并进入该锁定主体(或锁定同一对象的其他锁定主体)。等待线程将保持其“等待”状态,直到脉冲。在脉冲之后的某个时间,它重新加入“就绪”队列和阻止,直到锁中没有“活动”线程。然后它从它停止的地方恢复。

答案 1 :(得分:5)

你锁定一个物体。如果另一个线程试图同时访问该对象标记的关键部分,它将阻塞,直到锁定被移除/完成。

示例:

public static object DatabaseLck= new object();

lock (DatabaseLck) {
        results = db.Query<T>(query).ToList();
     }

lock (DatabaseLck) {
       results = db.Query<T>(string.Format(query, args)).ToList();
  }

这些代码块中的任何一个都不能同时运行,因为它们使用相同的锁对象。如果您为每个锁定对象使用了不同的锁定对象,则它们可以同时运行。

答案 2 :(得分:3)

这是同一个关键部分。

lock (synclock)
{
  // the critical section protected by the lock statement
  // Only one thread can access this at any one time
}

请参阅MSDN上的lock Statement

  

lock关键字通过获取给定对象的互斥锁,执行语句,然后释放锁来将语句块标记为关键部分。


  

或者是否意味着:lock关键字确保一个线程不会进入任何关键的代码段,而另一个线程位于任何关键部分。 ?

即可。这并不意味着。它表示受该锁定保护的关键部分和单独的锁定。


更新,代码示例如下:

如果使用单个对象锁定,它将锁定所有关键部分,导致其他线程阻塞,直到被释放。在您的代码示例中,一旦输入MethodA中的锁定,到达该锁定的所有其他线程和MethodB上的锁定将被阻塞,直到锁定被释放(这是因为您锁定了两种方法中的相同对象。)

答案 3 :(得分:1)

这并不意味着任何,尽管您可以通过使用同一个对象锁定它们来保护2个代码块同时不被多个线程输入。这是一种常见的范例 - 您可能希望锁定集合以进行清除和写入。

答案 4 :(得分:0)

不,这意味着另一个线程不会进入受此锁定语句保护的临界区。

“关键”部分仅由程序员定义,在这种情况下,您可以将其替换为:受锁定保护的部分

所以翻译: lock关键字确保一个线程不会输入受lock保护的代码段,而另一个线程位于此段代码中(受lock保护)

答案 5 :(得分:0)

它所讨论的关键部分是由lock语句保护的部分。

任何锁定在同一对象上的关键部分都将被阻止访问。

锁对象也是静态的也很重要,因为锁需要锁定(或试图锁定)锁对象的同一个实例。