C# 如何锁定多个对象?

C# 如何锁定多个对象?,c#,C#,我想同时锁定两个对象。 为什么我不能像这样写代码 锁(obj1、obj2) 我应该一直这样写吗 lock (obj1) { lock (obj2) { } } 也许这可以简化一些? 很可能最好引入特殊的私有对象并将其用于锁定…如果您编写这样的代码,您需要确保始终按此顺序锁定这两个对象。否则,您可能会遇到死锁。这是锁定多个对象的正确方法,是的 我的猜测是,在lock语句中只允许一个参数的原因是为了使锁的获取顺序尽可能清晰 请注意,您必须确保这两个锁在代码中的任何地方都是按

我想同时锁定两个对象。 为什么我不能像这样写代码

锁(obj1、obj2)

我应该一直这样写吗

lock (obj1)
{
    lock (obj2)
    {
    }
}
也许这可以简化一些?
很可能最好引入特殊的私有对象并将其用于锁定…

如果您编写这样的代码,您需要确保始终按此顺序锁定这两个对象。否则,您可能会遇到死锁。

这是锁定多个对象的正确方法,是的

我的猜测是,在lock语句中只允许一个参数的原因是为了使锁的获取顺序尽可能清晰

请注意,您必须确保这两个锁在代码中的任何地方都是按相同的顺序执行的,否则可能会出现死锁


正如您所建议的,您还可以引入一个专用的锁对象,但这会使您的锁更加粗糙。这完全取决于你的需要。如果您有时只需要其中一个锁,则应将它们分开(但请确保保留锁顺序,如上所述)。

而不是锁定对象本身,您可以创建一个名为挂锁或类似对象的专用对象,并且只在需要的地方锁定该对象。

这里的锁定并不意味着在锁定期间,其他线程上的其他代码无法访问或修改该对象。如果锁定对象,任何其他线程都可以同时修改该对象。锁定代码块允许您做的是使锁定块内的代码成为单个条目,即只有一个线程可以执行一次锁定代码块,而尝试执行同一代码块的其他线程必须等待所有者线程执行完代码块。因此,在通常情况下,基本上不需要锁定2个或更多对象。通过锁定,您的目的是使代码块成为单个条目

您必须在编写代码时执行此操作的原因是,您不能同时锁定两个对象;您可以一个接一个地锁定它们(保持锁定顺序非常重要,否则可能会陷入死锁),最好尽可能明确地处理这些事情。

执行类似的操作

    internal static void DuoEnter(object po1, object po2, int pnTimeOutMs = 1000)
    {
        if ((po1 == null) && (po2 == null))
            return;
        int nMaxLoops = 100 * pnTimeOutMs;
        bool lOneProcessor = Environment.ProcessorCount < 2;
        for (int nLoops = 0; nLoops < nMaxLoops; nLoops++)
        {
            if ((po1 == null) || (po2 == null) || (po1 == po2))
            {
                if (Monitor.TryEnter(po1 ?? po2))
                    return;
            }
            else
            {
                if (Monitor.TryEnter(po1))
                    if (Monitor.TryEnter(po2))
                        return;
                    else
                        Monitor.Exit(po1);
            }
            if (lOneProcessor || (nLoops % 100) == 99)
                Thread.Sleep(1); // Never use Thread.Sleep(0)
            else
                Thread.SpinWait(20);
        }
        throw new TimeoutException(
            "Waited more than 1000 mS trying to obtain locks on po1 and po2");
    }

    internal static void DuoExit(object po1, object po2)
    {
        if ((po1 == null) && (po2 == null))
            return;
        if (po1 == null || po2 == null || po1 == po2)
            Monitor.Exit(po2 ?? po1);
        else
        {
            Monitor.Exit(po2);
            Monitor.Exit(po1);
        }
    } 
内部静态void DuoEnter(对象po1、对象po2、int-pnTimeOutMs=1000)
{
如果((po1==null)和&(po2==null))
返回;
int nMaxLoops=100*pnTimeOutMs;
bool lOneProcessor=Environment.ProcessorCount<2;
对于(int nLoops=0;nLoops
好吧,这个问题太老了,但是,我发现这是一个紧凑的问题,两个代码最终都会得到相同的编译语句(这个和问题描述中的一个):


我也遇到过同样的问题,我写了这段代码可能会帮助你,尽管它还远远不够完美:

private void MultiLock(object[] locks, WaitCallback pFunc, int index = 0)
{
    if (index < locks.Count())
    {
        lock (locks[index])
        {
            MultiLock(locks, pFunc, index + 1);
        }
    }
    else
    {
        ThreadPool.QueueUserWorkItem(pFunc);
    }
}

为什么?我想你只需要一把锁。出于好奇,可能是重复的:当我们锁上挂锁时,它是否也会锁上所有的内容?因此,如果挂锁引用的是
MyClass
的实例,那么其他线程现在可以锁定该实例吗?@Jakub:可以。用
lock
锁定
挂锁
实例不会锁定其中包含的任何对象。这是一个有趣的想法,但感觉有点像我正从窗户潜入一座前门被木板封住的建筑。如果像这样的东西是个好主意,为什么不将它烘焙到C#proper中呢?只需编写
lock(obj1){lock(obj2){…}
锁定并不会阻止任何其他线程访问该对象,但它会阻止任何其他线程进入锁定同一对象的任何代码块,不管它是否是相同的代码块。需要嵌套的lock语句是很常见的,因为代码块经常需要获取多个资源的锁,这些资源的锁是独立管理的。您对“到处都是相同的订单”的评论很有价值,我想在两次电汇中,对于相同的两个帐户a到B和B到a,例如,您将始终按字母顺序锁定,因此无论源帐户和目标帐户是什么,您都将锁定A和B,否则,锁定(A)锁定(B)和锁定(B)锁定(A)将死锁,即使您执行仅涉及一个帐户的操作(如取款),它也会起作用。我只是想在这里分享一个实际的例子,它让我疯狂了一段时间。问题是递归调用可能会产生一个很长的调用堆栈(如果有很多锁的话)。锁定动态数量的对象是多么有创意的/天才的解决方案啊!如果您以不同的顺序将对象传递给此方法,这很容易导致死锁:
MultiLock(new[]{a,b},…)
MultiLock(new[]{b,a},…)
同时为什么必须从
private void MultiLock(object[] locks, WaitCallback pFunc, int index = 0)
{
    if (index < locks.Count())
    {
        lock (locks[index])
        {
            MultiLock(locks, pFunc, index + 1);
        }
    }
    else
    {
        ThreadPool.QueueUserWorkItem(pFunc);
    }
}
public object LockedObject1 = new Object();
public object LockedObject2 = new Object();

public void MyFunction(object arg)
{
    WaitCallback pFunc = delegate
    {
        // Operations on locked objects
    }

    MultiLock(new object[] {LockedObject1, LockedObject2}, pFunc);
}