在C#中命名为Lock Collection?

cap*_*aig 4 c# synchronization mutex

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

有办法专门锁定任意键会很好:

string id = GetNextId();
AquireLock(id);
try
{
    DoDangerousThing();
}
finally
{
    ReleaseLock(id);
}
Run Code Online (Sandbox Code Playgroud)

如果没有其他人试图锁定相同的密钥,我希望他们能够同时运行.

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

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

xxb*_*bcc 7

您可以尝试使用a 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
}
Run Code Online (Sandbox Code Playgroud)

  • 这基本上是我要编写的答案,但我认为没有必要验证oLockInstance不是null.GetOrAdd永远不应该返回null,因为你总是从你提供的lambda返回一个有效的对象. (2认同)
  • @captncraig 这可能是可能的,但我认为这会非常困难,因为一个锁可能一次被一个或多个线程使用 - 您可以从字典中删除它(线程保留对象实例)但是如果一个新线程进来需要相同的实例,它现在将创建一个新实例,并且您将有多个并发线程锁定在独立的锁实例上。 (2认同)

Edo*_* B. 5

您可以使用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)))
            {
                try
                {
                    // ... open, use, and close resource identified by resourceId ...
                    // ...
                }
                finally
                {
                    // 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 (result = (_lockDict.TryGetValue(resourceId, out lockObjCheck) &&
                            object.ReferenceEquals(lockObj, lockObjCheck)))
            {
                try
                {
                    // ... close resource identified by resourceId ...
                    // ...
                }
                finally
                {
                    // This must be the LAST statement
                    _lockDict.TryRemove(resourceId, out lockObjCheck);
                }
            }
        }
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)