简体   繁体   中英

How to make a call internal collections thread-safe?

I have such class composition. Class declares a dictionary of objects that themselves have an internal dictionary objects.

    public class ValueObject
    {
        public float value = 0;
    }

    public class ValueDictionaryObject
    {
        private Dictionary<int, ValueObject> innerDict;

        public float GetTotalValue()
        {
            lock (((ICollection)this.innerDict).SyncRoot)
            {
                float total = 0f;
                foreach (var p in innerDict)
                    total += p.Value.value;
                return total;
            }
        }
    }

    public class OuterDictionaryObject
    {
        private Dictionary<int, ValueDictionaryObject> outerDict;

        public float GetTotalValueForSomeKey(int key )
        {
            lock (((ICollection)this.outerDict).SyncRoot)
            {
                return outerDict[key].GetTotalValue();
            }
        }
    }


    var outer = new OuterDictionaryObject();

I have problems when call from multiple threads, it seems happens deadlock

float result = outer.GetTotalValueForSomeKey(key);

How make this call thread safe?

It's not clear from the code you posted why you'd get a deadlock. Yes, you have nested lock acquisition, which is a prerequisite for deadlock. But there's nothing here to show us how you've inverted the lock acquisition order in some scenario relative to the one we see here.

Still...

You should try to avoid calling anything as much as possible while holding a lock. Sometimes it's unavoidable (eg accessing a member of a collection you're synchronizing access to, which means you have to call a member of that collection). But you should only make calls when that's essential to the problem you're solving.

Here, it seems like you could have just as easily written the code like this:

public class OuterDictionaryObject
{
    private Dictionary<int, ValueDictionaryObject> outerDict;

    public float GetTotalValueForSomeKey(int key )
    {
        ValueDictionaryObject vdo;

        lock (((ICollection)this.outerDict).SyncRoot)
        {
            vdo = outerDict[key];
        }

        return vdo.GetTotalValue();
    }
}

Using ConcurrentDictionary would have a similar effect, in that you would not be calling the GetTotalValue() method until the synchronized access to the dictionary object had completed. So IMHO that would be a good option too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM