59

我有 3 个问题需要帮助。

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

    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

4 回答 4

82

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

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

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 应该实现 IDisposable以便处理句柄,因为如果你不释放它,那么有人可能不会得到他们的咖啡。

不过也有一些问题——如果咖啡不够,我们应该做更多——这需要很长时间。见鬼 - 分配咖啡需要很长时间,这就是为什么我们要小心保护我们的资源。现在你在想,真的所有这些咖啡机的东西都应该在它自己的线程中,并且应该有一个事件在咖啡完成后被触发,然后它开始变得复杂,你明白知道的重要性你要锁定什么以及何时锁定,这样你就不会因为你问有多少杯而阻止制作咖啡。

如果“死锁”、“原子”、“监视器”、“等待”和“脉冲”这些词对你来说听起来都很陌生,你应该考虑阅读多处理/多线程的一般知识,看看你是否能解决公平的理发店问题哲学家就餐问题,都是资源争用的典型例子。

于 2012-12-07T14:00:18.127 回答
36

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() 将在持续时间 s 内产生锁。

于 2012-12-07T13:26:07.263 回答
6

1:您使用的对象定义/由您尝试强制执行的锁定粒度定义。如果 is 是“任何针对当前实例的调用”,那么 aprivate readonly object syncLock = new object()将是合理的。如果它是“任何代码,无论实例如何”(特别是静态的),那么private readonly static object syncLock = new object(). 有时,您试图保护的明显“事物”也会起作用:列表、队列等。主要的错误决定是:您已经泄漏到实例之外。thistypeof(...)stringlock

2:从当前线程Monitor.Wait 释放锁,等待“脉冲”或超时,此时它唤醒并加入队列以重新获得它拥有的锁(注意“s”用于重新进入) . 这意味着两个线程可以通过脉冲和等待在它们之间使用 aMonitor发出信号。

3:不相关;但基本上是“定期检查标志,并在被脉冲时”

于 2012-12-07T13:28:34.713 回答
3

根据MSDN 文档

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

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

于 2015-08-25T16:53:00.760 回答