在C#中命名为Lock Collection?

时间:2014-11-03 22:28:25

标签: c# synchronization mutex

我有多个线程将数据写入公共源,我希望两个线程相互阻塞,当且仅当它们触及相同的数据时。

有一种方法可以专门锁定任意键:

string id = GetNextId();
AquireLock(id);
try
{
    DoDangerousThing();
}
finally
{
    ReleaseLock(id);
}

如果没有其他人试图锁定同一个密钥,我希望他们可以同时运行。

我可以使用一个简单的互斥体字典来实现这一点,但我需要担心驱逐旧的,未使用的锁,如果集合变得太大,这可能会成为一个问题。

是否存在此类锁定模式的现有实现。

3 个答案:

答案 0 :(得分:7)

您可以尝试使用ConcurrentDictionary<string, object>创建命名对象实例。当您需要一个新的锁定实例(之前没有使用过)时,您可以将它添加到字典中(通过GetOrAdd添加是一个原子操作)然后所有线程可以共享同一个命名对象你可以根据你的数据从字典中提取它。

例如:

// Create a global lock map for your lock instances.
public static ConcurrentDictionary<string, object> GlobalLockMap =
    new ConcurrentDictionary<string, object> ();

// ...

var oLockInstance = GlobalLockMap.GetOrAdd ( "lock name", x => new object () );

if (oLockInstance == null)
{
    // handle error
}

lock (oLockInstance)
{
    // do work
}

答案 1 :(得分:4)

lock关键字(MSDN)已经这样做了。

当你锁定时,你传递对象以锁定 on

lock (myLockObject)
{
}

这使用Monitor类和特定对象在同一对象上使用lock同步任何线程。

由于字符串文字是“实习”的 - 也就是说,它们被缓存以便重用,因此具有相同值的每个文字实际上都是同一个对象 - 您也可以对字符串执行此操作:

lock ("TestString")
{
}

由于你不是处理字符串文字,你可以按照C#: Strings with same contents中的描述实习你所读取的字符串。

如果使用的引用是从一个实习字符串(文字或明确实习)中复制(直接或间接),它甚至可以工作。但我不推荐它。这非常脆弱,并且可能导致难以调试的问题,因为可以轻松地创建与实习字符串具有相同值的字符串的新实例。

只有当其他东西进入同一对象上的锁定部分时,锁才会阻止。因此,不需要保留字典,只需要适用的锁定对象。

但实际上,您需要维护ConcurrentDictionary或类似内容,以允许您的对象访问相应的锁定对象。

答案 2 :(得分:2)

您可以使用ConcurrentDictionary<string, object>创建和重用不同的锁。如果要从字典中删除锁,并且还要在将来重新打开相同的命名资源,则必须在关键区域内检查先前获取的锁是否已被其他线程删除或更改。在离开关键区域之前,请注意从最后步骤中删除字典中的锁定。

    static ConcurrentDictionary<string, object> _lockDict =
        new ConcurrentDictionary<string, object>();

    // VERSION 1: single-shot method

    public void UseAndCloseSpecificResource(string resourceId)
    {
        bool isSameLock;
        object lockObj, lockObjCheck;
        do
        {
            lock (lockObj = _lockDict.GetOrAdd(resourceId, new object()))
            {
                if (isSameLock = (_lockDict.TryGetValue(resourceId, out lockObjCheck) && 
                                  object.ReferenceEquals(lockObj, lockObjCheck)))
                {
                    // ... open, use, and close resource identified by resourceId ...

                    // This must be the LAST statement
                    _lockDict.TryRemove(resourceId, out lockObjCheck);
                }
            }
        }
        while (!isSameLock);
    }

    // VERSION 2: separated "use" and "close" methods
    //            (can coexist with version 1)

    public void UseSpecificResource(string resourceId)
    {
        bool isSameLock;
        object lockObj, lockObjCheck;
        do
        {
            lock (lockObj = _lockDict.GetOrAdd(resourceId, new object()))
            {
                if (isSameLock = (_lockDict.TryGetValue(resourceId, out lockObjCheck) && 
                                  object.ReferenceEquals(lockObj, lockObjCheck)))
                {
                    // ... open and use (or reuse) resource identified by resourceId ...
                }
            }
        }
        while (!isSameLock);
    }

    public bool TryCloseSpecificResource(string resourceId)
    {
        bool result = false;
        object lockObj, lockObjCheck;
        if (_lockDict.TryGetValue(resourceId, out lockObj))
        {
            lock (lockObj)
            {
                if (_lockDict.TryGetValue(resourceId, out lockObjCheck) && 
                    object.ReferenceEquals(lockObj, lockObjCheck))
                {
                    result = true;
                    // ... close resource identified by resourceId ...

                    // This must be the LAST statement
                    _lockDict.TryRemove(resourceId, out lockObjCheck);
                }
            }
        }
        return result;
    }