-1

I am writing a c# class as below. I have roll number of the student as a unique key in a table is there any way I can lock based on rollNo, if modification from multiple threads with the same roll number is not allowed and with different roll number it should be allowed.

Class ABC 
{
    public Void UpdateStudent(int rollNo)
    {
        student = fetchRecord();

        if(some condtion)
        {
            //Update Student table
        }

        if(some condtion)
        {
            //Update Student table
        }

        if(some condtion)
        {
            //Update Student table
        }

        // enter code here
    }
}
DaggeJ
  • 2,094
  • 1
  • 10
  • 22

2 Answers2

0

I would do it with a Dictionary of locks. So for each roll number, you have a lock object and then you use it for safely updating the table only on one thread at a time.

Class ABC 
{
    private Dictionary<int, object> locks = new Dictionary<int, object>();
    private object locks_lock = new object();

    private object GetLock(int rollNo)
    {
        object lock;
        lock(locks_lock) {
            // get the lock for the provided rollNo
            if(!locks.TryGetValue(rollNo, out lock)) {
                // the lock for this rollNo doesn't exist yet, create it now
                lock = new object();
                locks.Add(rollNo, lock);
            }
        }
        return lock;
    }

    public void UpdateStudent(int rollNo)
    {
        var student = fetchRecord();

        if(some condition) {
            lock(GetLock(rollNo)) {
                //Update Student table
            }
        }
        if(some condition) {
            lock(GetLock(rollNo)) {
                //Update Student table
            }
        }

        // enter code here ...

    }
}
GregorMohorko
  • 2,739
  • 2
  • 22
  • 33
  • 1
    This has a couple of problems though: (a) you need to lock the access to the dictionary as well (when you `Add` the "locks"). (b) by having a one-to-one relationship between rollNo and lock, you will keep an entry for a student (or the rollNo) as long as the instance of the outer class exists. Group rollNo into buckets and have a lock for each. This will lock "a group of students" at once, yes, but you won't have to keep an unbounded number of entries in the dictionary (for maybe, forever). – Christian.K Apr 14 '20 at 11:33
  • I fixed the (a) problem. Regarding the (b) problem: I don't think this is a problem memory-wise nor implementation-wise. Retrieving a value from the Dictionary is very fast, close to O(1). – GregorMohorko Apr 14 '20 at 12:24
  • Maybe, but there are other solutions that don't have that "problem" in the first place. YMMV of course. – Christian.K Apr 14 '20 at 15:47
  • This will work under the assumption that the "Student table" is a thread-safe container, so that it can be mutated by multiple threads in parallel. Which is unlikely IMHO. – Theodor Zoulias Apr 14 '20 at 16:58
0

You could hold locks with a ConcurrentDictionary.

public class ABC
{
    static readonly ConcurrentDictionary<int, Lazy<object>> Locks = new ConcurrentDictionary<int, Lazy<object>>();

    public void UpdateStudent(int rollNo)
    {

        lock (Locks.GetOrAdd(rollNo, _ => new Lazy<object>()))
        {
            // do work here
            Console.WriteLine("Updating student");
            Thread.Sleep(1000);
            Locks.TryRemove(rollNo, out _);
        }
        Console.WriteLine("Student updated");
    }
}
Tjaart
  • 3,912
  • 2
  • 37
  • 61
  • 1
    Note: GetOrAdd is not thread-safe: https://andrewlock.net/making-getoradd-on-concurrentdictionary-thread-safe-using-lazy/ – NanoWar Oct 06 '21 at 09:31
  • thanks for that. I was pretty sure I tested it, but can't remember how. I've applied the `Lazy` trick as suggested in the link – Tjaart Oct 06 '21 at 13:06