Justin
Justin

Reputation: 6549

Ideal solution to make class with several properties thread safe

I have a class that represents a table in a datebase with each property of the class mapping to a column in the database. This class has several properties right now, 57 in fact. I need to share a list of these devices between a few threads.

My first question is what type of collection should I use that is best like a list to share between the threads. From looking in the System.Collections.Concurrent namespace, I believe a ConcurrentBag best suits my need, but if there are any other recommendations, I am open to it.

Secondly, the values assigned to the properties of my class will change over time. I have seen examples of putting locks in the get and set blocks of the properties, but I wanted to know if there was a cleaner way than declaring 57 different lock variables and putting locks all in my code.

Upvotes: 0

Views: 99

Answers (1)

David
David

Reputation: 10708

First, if you're concerned with concurrent access, you may want to consider making a single object to lock against for the whole class, resulting in "any change needs to have exclusive access" behavior. This depends on how ok you are with the performance hit of every write waiting on a single write, and how often different properties change at once or rely on each other's values.

Another solution is write a wrapper type, and for each underlying private *** _value field, wrap it in said wrapper, which can handle a single-lock-per-object, allowing minimal duplication of code with the same duplication of performance. this will, however, require each property to have an explicit body and underlying field, rather than letting the compiler do the work, so be aware of that trade-off. Also be aware that by wrapping it in a class using this could subtly change some of the behavior of value types, though a simple pass-in pass-out like this shouldn't see any such effects.

class ConcurrencyWrapper<T>
{
    priavte object _lockObject = new object();
    private T _value;
    public T Value
    {
        get { lock(_lockObject) return _value; }
        set { lock(_lockObject) _value = value; }
    }
}

As pointed out by @Patrick Hofman (see comments), you can also utilize a dictionary of lockable objects, and access them according to the name of each property via the CallerMemberNameAttribute

private ConcurrentDictionary<string, object> lockDict = new ConcurrentDictionary<string, object>();
private object GetLock([CallerMemberName] string name)
{
    return lockDict.GetOrAdd(name,
        (key) => new object());    // Using a lambda here prevents evaluation each time the dictionary is queried
}
...
private string _someProperty;
public string SomeProperty
{
    get { lock(GetLock()) return _someProperty; }
    set { lock(GetLock()) _someProperty = value; }
}

Finally, if you have lots of concurrent read access but only a few writes, take a look at the ReaderWriterLockSlim class, as this allows for concurrent reads but singular writes. Be aware this will require more verbosity than lock(_lockObject) because you'll need to perform the entire try { } catch { } finally { } setup yourself.

Upvotes: 3

Related Questions