Pupkin
Pupkin

Reputation: 1101

How to lock objects withthe same ids?

I have got the following code:

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; 
}

Method Update can be performed in two or more threads and I use lock to prevent cuncurency problems with foo. But I would like to lock only those Foo that have similar Id. For instance if one thread executes method Update with Foo.Id = 1 and another thread executes Update with Foo.Id = 2 then lock is not needed and if two threads execute Update with two instances Foo with the same Id, lock is needed. Is it possible to create a such lock?

Upvotes: 8

Views: 1189

Answers (4)

Mark Cilia Vincenti
Mark Cilia Vincenti

Reputation: 1614

I know the question is very old, but in case someone stumbles upon this and has the same issue, take a look at AsyncKeyedLock which solves the problem. Note: I am the author of this library.

In case you're using asynchronous code you can use:

using (await _locker.LockAsync(myObject.Id))
{
   ...
}

or if you're using synchronous code:

using (_locker.Lock(myObject.Id))
{
   ...
}

Upvotes: 1

Tim Schmelter
Tim Schmelter

Reputation: 460108

You could use this class to get a lock object for every 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;
        }
    }
}

Then hold an instance of it in your class:

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

Usage is simple:

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

Upvotes: 5

Fabjan
Fabjan

Reputation: 13676

You could use ConcurrentDictionary<TKey, TValue> for storing your locks.

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

And use it in Update method:

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
    }
}

Upvotes: 3

Henk Holterman
Henk Holterman

Reputation: 273244

You can just lock(foo) when you're sure

  • no other code is using foo to lock on
  • reference equality (identity) is acceptable/usable, this is not using Id

You can not lock on int Id.


Part 2,

Alas I use different instances of Foo with the same Id

Then you could try a ConcurrentDictionary<int, object> to store a different _locker per Id. But that adds some overhead, and the problem of cleaning up that Dictionary when you run it for longer periods.

Upvotes: 2

Related Questions