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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13763917/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 09:43:53  来源:igfitidea点击:

C# lock statement, what object to lock on?

c#multithreadinglocking

提问by user1885498

I have 3 questions that I need help with.

我有 3 个问题需要帮助。

  1. What are the correct objects/references to be passed as lockstatement parameter? I've seen a lot of sample codes and I noticed that the objects/references passed in could possibly be non related to the current class or any other class in the program as long as the access modifier staticis non public? E.g.:

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

    It just doesn't make sense to me.

  2. I found a sample code in MSDN about multi threading that uses lockstatements too. In the sample there are two try/catchblocks with the Monitor.Wait()within it. If I understand the logic correctly, the readerFlagwill forbid the program to ever enter the try/catchblock at all.
    The code is example 2 from here:
    http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx

  3. How do I run a thread that runs in the background as long as the Windows Form is active?

  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 窗体处于活动状态,如何运行在后台运行的线程?

回答by Henk Holterman

1) your code is incomplete. You always lock around a certain (shared) resource. The anyObjectshould have a close 1-1 correspondence in lifetime with that shared object.

1)你的代码不完整。您总是锁定某个(共享)资源。的anyObject应在与该共享对象寿命密切1-1的对应关系。

For instance:

例如:

a) the simple but most direct pattern:

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

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

there is a drawback in this pattern: what if other code also locks on sharedListfor other reasons? Usually not a practical problem, but it is the reason that the recommended pattern is (b):

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

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

Or, when the shared object is static (c) :

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

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

2) The threads alternate setting readerFlagto true or false so the try/catch blocks will be entered. The synchronization is done with Monitor.Pulse() and .Wait(). Note that Wait() will yield the lock for the duration s there is no deadlock.

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

回答by Marc Gravell

1: the object you use defines / is-defined-by the lock granularity you are trying to enforce. If is is "anything calling against the current instance", then a private readonly object syncLock = new object()would be reasonable. If it is "any code, regardless of the instance" (static, in particular), then private readonly static object syncLock = new object(). Sometimes there is an obvious "thing" you are trying to protect that will also serve: a list, a queue, etc. The main wrongdecisions are: this, typeof(...), any string, any value-type that you are boxing for each lock, and anything that you have leaked outside of the instance.

1:您使用的对象定义/由您尝试强制执行的锁粒度定义。如果 is 是“任何调用当前实例的东西”,那么 aprivate readonly object syncLock = new object()将是合理的。如果它是“任何代码,无论实例如何”(特别是静态的),那么private readonly static object syncLock = new object(). 有时,您试图保护的一个明显的“事物”也将提供:列表、队列等。主要的错误决定是:this, typeof(...), any string,您为 each 装箱的任何值类型lock,以及任何你已经泄漏到实例之外。

2: Monitor.Waitreleasesthe locks from the current thread, waiting for either a "pulse" or a timeout, at which point it wakes up and joins the queue to regainto locks it had (note the "s" there is for re-entrancy). That means that two threads can use a Monitorto signalbetween themselves, by pulsing and waiting.

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

3: unrelated; but basically "check a flag periodically, and when being pulsed"

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

回答by plinth

How and what you lock on depends upon what you're doing.

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

Let's say that you're working with a device of some kind - say a coffee maker. You might have a class that looks like this:

假设您正在使用某种设备 - 例如咖啡机。你可能有一个看起来像这样的类:

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

In this case, you are protecting access to the _coffeeHandle - a pointer/handle to a real physical device, so this is pretty easy:

在这种情况下,您正在保护对 _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
    }
 }

So if I'm running a multithreaded app, I (probably) don't want to read the number of cups that are available while I'm dispensing (maybe it's a hardware error). By protecting accesses to the handle, I can ensure that. Also, I can't be asked to dispense while I'm already dispensing - that would be bad, so that's protected too. Finally, I don't dispense unless I have enough coffee available and you notice that I don'tuse my public property to check that - this way the action of ensuring there's enough coffee and dispensing are tied together. The magic word is atomic - they can't be cut apart without creating issues.

因此,如果我正在运行一个多线程应用程序,我(可能)不想在分配时读取可用的杯子数量(可能是硬件错误)。通过保护对句柄的访问,我可以确保这一点。此外,当我已经在分配时,不能要求我分配 - 那会很糟糕,因此也受到保护。最后,除非我有足够的咖啡可用,否则我不会分配,并且您注意到我使用我的公共财产来检查这一点——这样确保有足够的咖啡和分配的行动是联系在一起的。神奇的词是原子的——它们不能在不产生问题的情况下被分开。

You use a static object as a lock if you have one and only one instance of a resource that needs protecting. Think, "do I have a singleton?" and that will be a guideline for when you might need a static lock. For example, let's say that CoffeeMaker has a private constructor. Instead, you have a factory method that constructs coffee machines:

如果您只有一个需要保护的资源实例,则可以使用静态对象作为锁。想一想,“我有单身吗?” 这将是您何时可能需要静态锁的指南。例如,假设 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);
    }
 }

Now in this case, it feels like CoffeeMaker should implement IDisposableso that handle gets taken care of, because if you don't release it then somebody might not be getting their coffee.

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

There are a few problems though - maybe if there's not enough coffee, we should make more - and that takes a long time. Heck - dispensing coffee takes a long time, which is why we're careful to protect our resources. Now you're thinking that really all this coffee maker stuff should be in a thread of its own and that there should be an event that gets fired when the coffee is done, and then it starts to get complicated and you understand the importance of knowing what you're locking on and when so that you don't block making coffee because you asked how many cups are there.

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

And if the words "deadlock", "atomic", "monitor", "wait", and "pulse" all sound foreign to you, you should consider reading up on multiprocessing/multithreading in general and see if you can solve the fair barbershop problemor the dining philosophers problem, both quintessential examples of resource contention.

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

回答by Vladimir T.

According the MSDN documentation:

根据MSDN 文档

The argument provided to the lockkeyword ... is used to define the scope of the lock. ...Strictly speaking, the object provided is used solely to uniquely identify the resource being shared among multiple threads, so it can be an arbitrary class instance. In practice, however, this object usually represents the resource for which thread synchronization is necessary.

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

In my case, I have passed the exact static object that I have needed to change.

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