如何使用相同的ID锁定对象?

时间:2017-10-23 11:46:51

标签: c# multithreading concurrency locking

我有以下代码:

public void Update(Foo foo)
{
    lock(_locker) 
    {
        UpdateFirstPart(foo.First);
        UpdateSecondPart(foo.Second);
        UpdateThirdPart(foo.Third);
    }
} 

public class Foo 
{
    public int Id;

    public Some1 First;  

    public Some2 Second; 

    public Some3 Third; 
}

方法Update可以在两个或多个线程中执行,我使用lock来防止foo出现问题。但我想只锁定那些Foo相似的Id。例如,如果一个线程使用Foo.Id = 1执行方法Update而另一个线程使用Foo.Id = 2执行Update,则不需要lock,如果两个线程执行带有两个的更新需要具有相同FooId的实例lock。是否可以创建这样的锁?

3 个答案:

答案 0 :(得分:3)

您可以使用此类为每个Id获取一个锁定对象:

public class MultiLockObjects<TKey>
{
    private readonly ConcurrentDictionary<TKey, Object>  _multiLocker = new ConcurrentDictionary<TKey, Object>();

    public Object this[TKey key]
    {
        get
        {
            Object lockObj = _multiLocker.GetOrAdd(key, tKey => new Object());
            return lockObj;
        }
    }
}

然后在你的班级中保留一个实例:

private MultiLockObjects<int> _idLocks = new MultiLockObjects<int>();

用法很简单:

public void Update(Foo foo)
{
    Object idLockObject = _idLocks[foo.Id];
    lock (idLockObject)
    {
        UpdateFirstPart(foo.First);
        UpdateSecondPart(foo.Second);
        UpdateThirdPart(foo.Third);
    }
}

答案 1 :(得分:3)

您可以使用ConcurrentDictionary<TKey, TValue>存储锁。

private static readonly ConcurrentDictionary<int, object> ThreadLockDict =
                                                    new ConcurrentDictionary<int, object>();

并在Update方法中使用它:

public static void Update(Foo foo)
{
    // add a new locker object for each foo if it's not already in dictionary
    ThreadLockDict.TryAdd(foo.Id, new object());

    lock (ThreadLockDict[foo.Id])
    {
        // some code
    }
}

答案 2 :(得分:2)

当您确定

时,您可以lock(foo)
  • 没有其他代码使用foo锁定
  • 引用相等(标识)是可接受/可用的,这不是使用Id

您无法锁定int Id

第2部分,

  

唉,我使用不同的Foo实例和相同的Id

然后你可以尝试ConcurrentDictionary<int, object>为每个Id存储不同的_locker。但是这会增加一些开销,以及在运行较长时间后清理该词典的问题。