我有3个问题需要帮助。
作为lock
语句参数传递的正确对象/引用是什么?我已经看过很多示例代码,我注意到传入的对象/引用可能与当前类或程序中的任何其他类无关,只要访问修饰符static
不公开? E.g:
private Object anyObj = new Object();
lock(anyObj){.....}
private static readonly object Locker = new object();
lock(Locker){.....}
这对我来说没有意义。
我在MSDN中找到了一个使用lock
语句的多线程示例代码。在示例中,有两个try
/ catch
块,其中包含Monitor.Wait()
。如果我理解正确的逻辑,readerFlag
将禁止程序进入try
/ catch
块。
代码是示例2从这里开始:
http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx
只要Windows窗体处于活动状态,如何运行在后台运行的线程?
答案 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 problem或the 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;你正试图保护它也会服务:列表,队列等。主要的错误的决定是:this
,typeof(...)
,任何string
,您为每个lock
装箱的任何值类型,以及您在实例外泄漏的任何值。
2:Monitor.Wait
释放来自当前线程的锁定,等待&#34;脉冲&#34;或者超时,此时它会唤醒并加入队列以重新获得以锁定它(注意&#34; s&#34;有重新入侵)。这意味着两个线程可以通过脉冲和等待在它们之间使用Monitor
到信号。
答案 3 :(得分:2)
提供给 lock 关键字的参数...用于定义锁的范围。 ......严格来说, 提供的对象仅用于唯一标识正在处理的资源 在多个线程之间共享,因此它可以是任意类 实例。然而,在实践中,这个对象通常代表了 需要线程同步的资源。
就我而言,我已经传递了我需要更改的确切静态对象。