C#lock语句,要锁定什么对象?

时间:2012-12-07 13:19:24

标签: c# multithreading locking

我有3个问题需要帮助。

  1. 作为lock语句参数传递的正确对象/引用是什么?我已经看过很多示例代码,我注意到传入的对象/引用可能与当前类或程序中的任何其他类无关,只要访问修饰符static不公开? E.g:

    private Object anyObj = new Object();
    lock(anyObj){.....}
    
    private static readonly object Locker = new object();
    lock(Locker){.....}
    

    这对我来说没有意义。

  2. 我在MSDN中找到了一个使用lock语句的多线程示例代码。在示例中,有两个try / catch块,其中包含Monitor.Wait()。如果我理解正确的逻辑,readerFlag将禁止程序进入try / catch块。
    代码是示例2从这里开始:
    http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx

  3. 只要Windows窗体处于活动状态,如何运行在后台运行的线程?

4 个答案:

答案 0 :(得分:74)

你锁定的方式和内容取决于你正在做什么。

假设您正在使用某种设备 - 比如咖啡机。您可能有一个如下所示的类:

public CoffeeMaker {
    private IntPtr _coffeeHandle;
    private Object _lock = new Object();
}

在这种情况下,您正在保护对_coffeeHandle的访问 - 对真实物理设备的指针/句柄,所以这很容易:

public int AvailableCups {
    get {
        lock (_lock) {
            return GetAvailableCups(_coffeeHandle); // P/Invoked
        }
    }
}

public void Dispense(int nCups)
{
    lock (_lock) {
        int nAvail = GetAvailableCups(_coffeeHandle);
        if (nAvail < nCups) throw new CoffeeException("not enough coffee.");
        Dispense(_coffeeHandle, nCups); // P/Invoked
    }
 }

因此,如果我正在运行多线程应用程序,我(可能)不想读取我在分配时可用的杯数(可能是硬件错误)。通过保护对句柄的访问,我可以确保。另外,在我已经配药的时候,我不能被要求免除 - 这会很糟糕,所以它也受到了保护。最后,除非我有足够的咖啡,否则你不会放弃,而且你注意到我使用我的公共财产来检查 - 这样确保足够的咖啡和配药的行动被捆绑在一起。这个神奇的词是原子的 - 如果不造成问题,它们就不能被分开。

如果您只有一个需要保护的资源实例,则使用静态对象作为锁。想想,“我有一个单身人士吗?”这将是您何时需要静态锁定的指南。例如,假设CoffeeMaker有一个私有构造函数。相反,你有一个构建咖啡机的工厂方法:

static Object _factLock = new Object();

private CoffeeMaker(IntPtr handle) { _coffeeHandle = handle; }

public static CoffeeMaker GetCoffeeMaker()
{
    lock (_factLock) {
        IntPtr _handle = GetCoffeeMakerHandle(); // P/Invoked
        if (_handle == IntPtr.Zero) return null;
        return new CoffeeMaker(_handle);
    }
 }

现在在这种情况下,感觉CoffeeMaker应该implement IDisposable,以便处理得到处理,因为如果你不释放它,那么有人可能没有得到他们的咖啡。

虽然有一些问题 - 也许如果没有足够的咖啡,我们应该做更多 - 这需要很长时间。哎呀 - 分配咖啡需要很长时间,这就是为什么我们要小心保护我们的资源。现在你认为真的所有这些咖啡机的东西应该是自己的一个线程,并且应该有一个事件在咖啡完成时被解雇,然后它开始变得复杂,你明白了解知识的重要性什么你锁定什么,什么时候你不要阻止煮咖啡因为你问了多少杯子。

如果“死锁”,“原子”,“监听”,“等待”和“脉冲”这些词对你来说都是陌生的,那么你应该考虑阅读多处理/多线程,看看你是否可以解决the fair barbershop problemthe dining philosophers problem,这两个都是资源争用的典型例子。

答案 1 :(得分:33)

1)您的代码不完整。您始终锁定某个(共享)资源。 anyObject在生命周期内与该共享对象的关系应该为1-1。

例如:

a)简单但最直接的模式:

List<MyClass> sharedList = ...;
...
lock (sharedList) { sharedList.Add(item); }

这种模式有一个缺点:如果其他代码因其他原因也锁定在sharedList上会怎样?通常不是一个实际问题,但这是推荐模式是(b):

的原因
List<MyClass> sharedList = ...;
private object listLock = new object();
...
lock (listLock) { sharedList.Add(item); }

或者,当共享对象是静态的时候(c):

static List<MyClass> sharedList = ...;
private static object listLock = new object();
...
lock (listLock) { sharedList.Add(item); }

2)线程将设置readerFlag替换为true或false,以便输入try / catch块。使用Monitor.Pulse()和.Wait()完成同步。请注意,Wait()将在没有死锁的持续时间内产生锁定。

答案 2 :(得分:6)

1:您使用的对象由您尝试强制执行的锁粒度定义/定义。如果是&#34;任何针对当前实例的调用&#34;,那么private readonly object syncLock = new object()将是合理的。如果它是&#34;任何代码,无论实例&#34; (特别是静态的),然后private readonly static object syncLock = new object()。有时会有一个明显的&#34;&#34;你正试图保护它也会服务:列表,队列等。主要的错误的决定是:thistypeof(...),任何string,您为每个lock装箱的任何值类型,以及您在实例外泄漏的任何值。

2:Monitor.Wait 释放来自当前线程的锁定,等待&#34;脉冲&#34;或者超时,此时它会唤醒并加入队列以重新获得以锁定它(注意&#34; s&#34;有重新入侵)。这意味着两个线程可以通过脉冲和等待在它们之间使用Monitor信号

3:无关;但基本上&#34;定期检查一个标志,并在脉冲时#34;

答案 3 :(得分:2)

根据MSDN documentation

  

提供给 lock 关键字的参数...用于定义锁的范围。 ......严格来说,   提供的对象仅用于唯一标识正在处理的资源   在多个线程之间共享,因此它可以是任意类   实例。然而,在实践中,这个对象通常代表了   需要线程同步的资源。

就我而言,我已经传递了我需要更改的确切静态对象。