锁定工厂方法

时间:2012-02-15 18:24:39

标签: c# locking factory-pattern

我正在与后端系统连接,在后端系统中,我必须永远不会有多个与给定对象的开放连接(由它的数字ID标识),但不同的消费者可能会彼此独立地打开和关闭它们。

粗略地说,我有一个像这样的工厂类片段:

private Dictionary<ulong, IFoo> _openItems = new Dictionary<ulong, IFoo>();
private object _locker = new object();

public IFoo Open(ulong id)
{
    lock (_locker)
    {
        if (!_openItems.ContainsKey(id))
        {
            _openItems[id] = _nativeResource.Open(id);
        }

        _openItems[id].RefCount++;

        return _openItems[id];
    }
}

public void Close(ulong id)
{
    lock (_locker)
    {
        if (_openItems.ContainsKey(id))
        {
            _openItems[id].RefCount--;
            if (_openItems[id].RefCount == 0)
            {
                _nativeResource.Close(id);
                _openItems.Remove(id);
            }
        }
    }
}

现在,这是问题所在。就我而言,_nativeResource.Open 非常慢。这里的锁定是相当天真的,并且当存在许多不同的并发。打开调用时可能非常慢,即使它们(很可能)指的是不同的ID并且不重叠,特别是如果它们不在_openItems中高速缓存中。

如何构建锁定,以便我只阻止并发访问到特定ID 而不是所有来电者?

2 个答案:

答案 0 :(得分:3)

您可能希望了解的是条带锁定策略。这个想法是你为M个项目共享N个锁(在你的情况下可能是ID),并选择一个锁,这样对于任何ID,所选择的锁总是相同的。为这种技术选择锁的经典方法是模除法 - 简单地将M除以N,取余数,并使用该索引的锁:

// Assuming the allLocks class member is defined as follows:
private static AutoResetEvent[] allLocks = new AutoResetEvent[10];


// And initialized thus (in a static constructor):
for (int i = 0; i < 10; i++) {
    allLocks[i] = new AutoResetEvent(true);
}


// Your method becomes
var lockIndex = id % allLocks.Length;
var lockToUse = allLocks[lockIndex];

// Wait for the lock to become free
lockToUse.WaitOne();
try {
    // At this point we have taken the lock

    // Do the work
} finally {
    lockToUse.Set();
}

答案 1 :(得分:1)

如果您使用的是.net 4,则可以尝试ConcurrentDictionary这些内容:

private ConcurrentDictionary<ulong, IFoo> openItems = new ConcurrentDictionary<ulong, IFoo>();
private object locker = new object();

public IFoo Open(ulong id)
{
    var foo = this.openItems.GetOrAdd(id, x => nativeResource.Open(x));

    lock (this.locker)
    {
        foo.RefCount++;
    }

    return foo;
}

public void Close(ulong id)
{
    IFoo foo = null;

    if (this.openItems.TryGetValue(id, out foo))
    {
        lock (this.locker)
        {
            foo.RefCount--;

            if (foo.RefCount == 0)
            {
                if (this.openItems.TryRemove(id, out foo))
                {
                    this.nativeResource.Close(id);
                }
            }
        }
    }
}

如果有人能看到任何明显的问题,请告诉我!