A static property in static class when used concurrently - c#

I have a static class 'Logger' with a public property called 'LogLevels' as in code below.
When the property is used concurrently in a multi-user or multi-threaded environment, could it cause problems?
Do I need to use thread synchronization for the code within the property 'LogLevels'?
public class Logger
{
private static List<LogLevel> _logLevels = null;
public static List<LogLevel> LogLevels
{
get
{
if (_logLevels == null)
{
_logLevels = new List<LogLevel>();
if (!string.IsNullOrWhiteSpace(System.Configuration.ConfigurationManager.AppSettings["LogLevels"]))
{
string[] lls = System.Configuration.ConfigurationManager.AppSettings["LogLevels"].Split(",".ToCharArray());
foreach (string ll in lls)
{
_logLevels.Add((LogLevel)System.Enum.Parse(typeof(LogLevel), ll));
}
}
}
if (_logLevels.Count == 0)
{
_logLevels.Add(LogLevel.Error);
}
return _logLevels;
}
}
}
UPDATE: I ended up using thread synchronization to solve concurrency problem in a static class, as in code below.
public class Logger
{
private static readonly System.Object _object = new System.Object();
private static List<LogLevel> _logLevels = null;
private static List<LogLevel> LogLevels
{
get
{
//Make sure that in a multi-threaded or multi-user scenario, we do not run into concurrency issues with this code.
lock (_object)
{
if (_logLevels == null)
{
_logLevels = new List<LogLevel>();
if (!string.IsNullOrWhiteSpace(System.Configuration.ConfigurationManager.AppSettings["SimpleDBLogLevelsLogger"]))
{
string[] lls = System.Configuration.ConfigurationManager.AppSettings["SimpleDBLogLevelsLogger"].Split(",".ToCharArray());
foreach (string ll in lls)
{
_logLevels.Add((LogLevel)System.Enum.Parse(typeof(LogLevel), ll));
}
}
}
if (_logLevels.Count == 0)
{
_logLevels.Add(LogLevel.Error);
}
}
return _logLevels;
}
}
}

When the property is used concurrently in a multi-user or multi-threaded environment, could it cause problems?
Absolutely. List<T> is not designed for multiple threads, except for the case where there are just multiple readers (no writers).
Do I need to use thread synchronization for the code within the property 'LogLevels'?
Well that's one approach. Or just initialize it on type initialization, and then return a read-only wrapper around it. (You really don't want multiple threads modifying it.)
Note that in general, doing significant amounts of work in a static constructor is a bad idea. Are you happy enough that if this fails, every access to this property will fail, forever?

This code posses race conditions and cannot be safely executed from multiple threads. The primary problem is the List<T> type is not thread safe yet this code will freely write to. This mean the writes can occur in parallel and hence break the implicit contract of List<T>

The short answer is "yes" and "yes" you do need threads synchronization.
The other question is, why re-invent the wheel? You can use something like log4net or .NET logging framework.

Related

Change and read properties of objects in ConcurrentDictionary in thread safe manner

I use ConcurrentDictionary to collect data in memory in web api application. Using api methods I add and update objects in ConcurrentDictionary. And there is background thread which analyze and clean up this dictionary based on object properties. Now I'm considering two approaches:
1. use lock on dictionary item in updateValueFactory in AddOrUpdate method, but question is how to read properties properly to be sure I have the latest version of it and that I'm not reading property in not stable state.
public class ThreadsafeService2
{
private readonly ConcurrentDictionary<string, ThreadSafeItem2> _storage =
new ConcurrentDictionary<string, ThreadSafeItem2>();
public void AddOrUpdate(string name)
{
var newVal = new ThreadSafeItem2();
_storage.AddOrUpdate(name, newVal, (key, oldVal) =>
{
//use lock
lock (oldVal)
{
oldVal.Increment();
}
return oldVal;
});
}
public void Analyze()
{
foreach (var key in _storage.Keys)
{
if (_storage.TryGetValue(key, out var item))
{
//how to read it properly?
long ticks = item.ModifiedTicks;
}
}
}
}
public class ThreadSafeItem2
{
private long _modifiedTicks;
private int _counter;
public void Increment()
{
//no interlocked here
_modifiedTicks = DateTime.Now.Ticks;
_counter++;
}
//now interlocked here
public long ModifiedTicks => _modifiedTicks;
public int Counter => _counter;
}
2. use Interlocked and memory barriers on property level without lock, looks a bit verbose for me.
public class ThreadsafeService1
{
private readonly ConcurrentDictionary<string, ThreadSafeItem1> _storage =
new ConcurrentDictionary<string, ThreadSafeItem1>();
public void AddOrUpdate(string name)
{
var newVal = new ThreadSafeItem1();
_storage.AddOrUpdate(name, newVal, (key, oldVal) =>
{
//no lock here
oldVal.Increment();
return oldVal;
});
}
public void Analyze()
{
foreach(var key in _storage.Keys)
{
if(_storage.TryGetValue(key, out var item))
{
//reading through interloacked
long ticks = item.ModifiedTicks;
}
}
}
}
public class ThreadSafeItem1
{
private long _modifiedTicks;
private int _counter;
public void Increment()
{
//make changes in atomic manner
Interlocked.Exchange(ref _modifiedTicks, DateTime.Now.Ticks);
Interlocked.Increment(ref _counter);
}
public long ModifiedTicks => Interlocked.Read(ref _modifiedTicks);
public int Counter => Thread.VolatileRead(ref _counter);
}
What is the best practices here?
So both of your implementations have major problems. The first solution locks when incrementing, but doesn't lock when reading, meaning the other places accessing the data can read invalid state.
A non-technical problem, but a major issue nonetheless, is that you've named your class ThreadSaveItem and yet it's not actually designed to be accessed safely from multiple threads. It's the callers responsibility, in this implementation, to ensure that the item isn't accessed from multiple threads. If I see a class called ThreadSafeItem I'm going to assume it's safe to access it from multiple threads, and that I don't need to synchronize my access to it so long as each operation I perform is the only thing that needs to be logically atomic.
Your Interlocked solution is problematic in that you have to fields that you're modifying, that are conceptually tied together, but you don't synchronize their changes together, meaning someone can observe a modification to one and not the other, which is a problem for that code.
Next, your use of AddOrUpdate in both solutions isn't really appropriate. The whole point of the method call is to add an item or replace it with another item, not to mutate the provided item (that's why it takes a return value; you're supposed to produce a new item). If you want to go with the approach of getting a mutable item and mutating it, the way to go would be to call GetOrAdd to either get an existing item or create a new one, and then to mutate it in a thread safe manner using the returned value.
The whole solution is radically simplified by simply making ThreadSafeItem immutable. It lets you use AddOrUpdate on the ConcurrentDictionary for the update, and it means that the only synchronization that needs to be done is the updating of the value of the ConcurrentDictionary, and it already handles synchronization of its own state, no synchronization needs to be done at all when accessing ThreadSafeItem, because all access to the data is inherently thread safe because it's immutable. This means that you never actually need to write any synchronization code at all, which is exactly what you want to strive for whenever possible.
And finally, we have the actual code:
public class ThreadsafeService3
{
private readonly ConcurrentDictionary<string, ThreadSafeItem3> _storage =
new ConcurrentDictionary<string, ThreadSafeItem3>();
public void AddOrUpdate(string name)
{
_storage.AddOrUpdate(name, _ => new ThreadSafeItem3(), (_, oldValue) => oldValue.Increment());
}
public void Analyze()
{
foreach (var pair in _storage)
{
long ticks = pair.Value.ModifiedTicks;
//Note, the value may have been updated since we checked;
//you've said you don't care and it's okay for a newer item to be removed here if it loses the race.
if (isTooOld(ticks))
_storage.TryRemove(pair.Key, out _);
}
}
}
public class ThreadSafeItem3
{
public ThreadSafeItem3()
{
Counter = 0;
}
private ThreadSafeItem3(int counter)
{
Counter = counter;
}
public ThreadSafeItem3 Increment()
{
return new ThreadSafeItem3(Counter + 1);
}
public long ModifiedTicks { get; } = DateTime.Now.Ticks;
public int Counter { get; }
}
The solution proposed by Servy (using an immutable Item type) is probably the best solution for your scenario. I would also suggest switching from class to readonly struct for reducing the allocations, although the ConcurrentDictionary is probably going to wrap the struct in a reference-type Node internally, so you might not gain anything from this.
For the sake of completeness I will propose an alternative solution, which is to use the GetOrAdd instead of the AddOrUpdate, and lock on the Item whenever you are doing anything with it:
public class Item // Mutable and thread-unsafe
{
public long ModifiedTicks { get; private set; }
public int Counter { get; private set; }
public void Increment()
{
ModifiedTicks = DateTime.Now.Ticks;
Counter++;
}
}
public class Service
{
private readonly ConcurrentDictionary<string, Item> _storage = new();
public void AddOrUpdate(string name)
{
Item item = _storage.GetOrAdd(name, _ => new());
lock (item) item.Increment(); // Dont't forget to lock!
}
public void Analyze()
{
foreach (var (key, item) in _storage.ToArray())
{
lock (item) // Dont't forget to lock!
{
long ticks = item.ModifiedTicks;
}
}
}
}
This solution offers probably the best performance, but the burden of remembering to lock correctly everywhere cannot be underestimated.
I can't comment on the specifics of what exactly you are doing, but interlock and Concurrent dictionary is better than locks you do yourself.
I would question this approach though. Your data is important enough, but not so important to persist it? Depending on the usage of the application this approach will slow it down by some degree. Again, not knowing exactly what you are doing, you could throw each "Add" into an MSMQ, and then have an external exe run at some schedule to process the items. The website will just fire and forget, with no threading requirements.

How can I avoid this deadlock in DevForce when using interceptors

We have recently started taking our business logic that relies heavily on DevForce and exposing it over a web API. We have been very careful to avoid threading issues by ensuring each request has its own set of entities, its own EntityManager, etc. However, we've started noticing logical deadlocks (in the .net code, not in SQL) when there are a lot of concurrent requests.
I've tracked down the problem to locking that is done by the PropertyInterceptors. We use them quite extensively and have cases where the interceptor on one property (Property A) will set another property (Property B) but that the converse is also true (setting B will also set A). The exact reasons for some of these cases are complicated but the basic idea is that we have some properties that we want to keep in sync. It seems that there is locking inside the PropertyInterceptor logic so we can easily hit deadlocks because the order in which those locks are taken can vary.
I've created a simple reproducible case below that involves an Entity with just two properties. One is an integer property and the other a string property. I have BeforeSet logic to keep these two in sync with each other. In the simple case of settings the properties one at a time, everything works. But since we are dealing with a web api, it's very common for things to execute in parallel. If we get one request that happens to set IntValue and another request that happens to set StringValue, we'll hit a deadlock. This is true even though we are talking about two different entities in two different EntityManagers. From our perspective, we are doing everything in a thread safe manner but then DevForce has some very long-lived locks which we know can be dangerous.
Here is the code which hopefully explains things. Keep in mind our actual code is much more complicated but the basic deadlock is the same:
public static void ReproduceDeadlock()
{
var e1 = new MyEntity();
var e2 = new MyEntity();
//This works - settings fields one at a time is fine
e1.IntValue = 1;
e2.StringValue = "2";
//But if we introduce some concurrency, we'll become deadlocked
Task.Run(() =>
{
//Wait a bit so e1.IntValue has a chance to start
Thread.Sleep(1000);
e2.StringValue = "22";
});
e1.IntValue = 11;
//Execution will never make it hear...setting the IntValue will never complete
}
public class MyEntity : Entity
{
[BeforeSet("StringValue")]
public void BeforeSetStringValue(PropertyInterceptorArgs<MyEntity, string> args)
{
//When the string is set, 'sync' it to the IntValue property
IntValue = int.Parse(args.Value);
}
[BeforeSet("IntValue")]
public void BeforeSetIntValue(PropertyInterceptorArgs<MyEntity, int> args)
{
//When the int is set, 'sync' it to the StringValue property
//Introduce a delay so the deadlock will obviously happen. In our real app, we don't have
// a Thread.Sleep() but we do have non-trivial logic that can cause just enough delay for the deadlock
// to happen sometimes
Thread.Sleep(2000);
StringValue = args.Value.ToString();
}
#region PropertyMetadata stuff
public class PropertyMetadata
{
public static readonly DataEntityProperty<MyEntity, string> StringValue =
new DataEntityProperty<MyEntity, string>("StringValue", true, false,
ConcurrencyStrategy.None, false, null,
false);
public static readonly DataEntityProperty<MyEntity, int> IntValue =
new DataEntityProperty<MyEntity, int>("IntValue", true, false,
ConcurrencyStrategy.None, false, null,
false);
}
public string StringValue
{
get { return PropertyMetadata.StringValue.GetValue(this); }
set { PropertyMetadata.StringValue.SetValue(this, value); }
}
public int IntValue
{
get { return PropertyMetadata.IntValue.GetValue(this); }
set { PropertyMetadata.IntValue.SetValue(this, value); }
}
#endregion
}
}
Stephen, maybe I do have a workaround for you. In the interceptor actions you can use SetValueRaw to sync the value to another property and avoid going through its interceptor (and validation). The method is available on the public IStructuralObject interface, which, although documented as for internal use only, is not something we plan to change. Both the EntityAspect and ComplexAspect classes implement this interface.
So your example would look like this:
[BeforeSet("StringValue")]
public void BeforeSetStringValue(PropertyInterceptorArgs<MyEntity, string> args)
{
//When the string is set, 'sync' it to the IntValue property
(this.EntityAspect as IStructuralObject).SetValueRaw(PropertyMetadata.IntValue, int.Parse(args.Value));
}
[BeforeSet("IntValue")]
public void BeforeSetIntValue(PropertyInterceptorArgs<MyEntity, int> args)
{
//When the int is set, 'sync' it to the StringValue property
//Introduce a delay so the deadlock will obviously happen. In our real app, we don't have
// a Thread.Sleep() but we do have non-trivial logic that can cause just enough delay for the deadlock
// to happen sometimes
Thread.Sleep(2000);
(this.EntityAspect as IStructuralObject).SetValueRaw(PropertyMetadata.StringValue, args.Value.ToString());
}
I'll note one other workaround too. The deadlock is in the interceptor, but you can have all the usual validation logic and change notification occur, just without the interception layer. One way to do this is to set the EntityGroup.PropertyInterceptionEnabled flag to false, but it's usually pretty clumsy to turn this on and off. Another option is a helper function to do exactly what the SetterInterceptor is doing:
public static void SetValueWithVerification(IStructuralObject so, DataEntityProperty property, object newValue)
{
if (so.VerifierEngine != null && so.VerifierEngine.Enabled && so.EntityGroup.VerificationEnabled)
{
if ((property.MemberMetadata.VerifierSetterOptions & VerifierSetterOptions.BeforeSet) > 0)
{
so.ValidatePropertyBeforeSet(property, newValue);
}
so.SetValueWithChangeNotification(property, newValue);
if ((property.MemberMetadata.VerifierSetterOptions & VerifierSetterOptions.AfterSet) > 0)
{
so.ValidatePropertyAfterSet(property, newValue);
}
}
else
{
so.SetValueWithChangeNotification(property, newValue);
}
}
Then call this in these tightly coupled interceptor actions:
SetValueWithVerification(this.EntityAspect, PropertyMetadata.StringValue, args.Value.ToString());

How to synchronize a collection of values in C#

I have inherited some code that has a set of real-time values that are captured over a serial link that runs on a separate thread:
class Data
{
public static int nFooCount;
public static decimal meanValue;
// Lots more of a variety of types.
}
The thread just stores the data into the field with no locking or other synchronization. Looks like a whole sea of race conditions to me. So I want to add some safety to it. The question is, what's best?
I could make the int fields volatile. Can't do that with the decimal types, though. Interlocked can help with that, albeit messily, using boxing. Or I could add a lock object
private static readonly object lockObj = new object();
and then accessor everything. But this locks all the fields even when it will only modify one at a time. I can't lock on primitives and that would be bad form anyway, and adding a lock object for every field would look wasteful. So is there a better way?
You could lock according to required access level using ReaderWriterLockSlim. You could do this better by turning them into a property:
public int Something
{
get {
locker.EnterReadLock();
try {
return something;
} finally {
locker.ExitReadLock();
}
}
set {
locker.EnterWriteLock();
try {
something = value;
} finally {
locker.ExitWriteLock();
}
}
}
This allows for multiple reads and single writes.

Global thread-safe multi-value custom Dictionary with single instantiation

I would like to have a global object similar to a multi-value Dictionary that is shared among different Threads.
I would like the object to be created only once (for example getting the data from a Database) and then used by the different Threads.
The Object should be easily extendable with additional properties (currently have only JobName and URL).
If possible, I would prefer to avoid locking.
I am facing the following issues:
The current version displayed below is not Thread safe;
I cannot use a ConcurrentDictionary since I have extended the Dictionary object to allow multiple values for each key;
This is the object structure that should be modified easily:
public struct JobData
{
public string JobName;
public string URL;
}
I have extended the Dictionary object to allow multiple values for each key:
public class JobsDictionary : Dictionary<string, JobData>
{
public void Add(string key, string jobName, string url)
{
JobData data;
data.JobName = jobName;
data.URL = url;
this.Add(key, data);
}
}
Static class that is shared among Threads.
As you can see it creates a Dictionary entry for the specific Job the first time it is called for that Job.
For instance, the first time it is called for "earnings" it will create the "earnings" dictionary entry. This creates issues with Thread safety:
public static class GlobalVar
{
private static JobsDictionary jobsDictionary = new JobsDictionary();
public static JobData Job(string jobCat)
{
if (jobsDictionary.ContainsKey(jobCat))
return jobsDictionary[jobCat];
else
{
String jobName;
String url = null;
//TODO: get the Data from the Database
switch (jobCat)
{
case "earnings":
jobName="EarningsWhispers";
url = "http://www.earningswhispers.com/stocks.asp?symbol={0}";
break;
case "stock":
jobName="YahooStock";
url = "http://finance.yahoo.com/q?s={0}";
break;
case "functions":
jobName = "Functions";
url = null;
break;
default:
jobName = null;
url = null;
break;
}
jobsDictionary.Add(jobCat, jobName, url);
return jobsDictionary[jobCat];
}
}
In each Thread I get the specific Job property in this way:
//Get the Name
string JobName= GlobalVar.Job(jobName).JobName;
//Get the URL
string URL = string.Format((GlobalVar.Job(jobName).URL), sym);
How can I create a custom Dictionary that is "instantiated" once (I know it is not the right term since it is static...) and it is Thread-safe ?
Thanks
UPDATE
Ok, here is the new version.
I have simplified the code by removing the switch statement and loading all dictionary items at once (I need all of them anyway).
The advantage of this solution is that it is locked only once: when the dictionary data is added (the first Thread entering the lock will add data to the dictionary).
When the Threads access the dictionary for reading, it is not locked.
It should be Thread-Safe and it should not incur in deadlocks since jobsDictionary is private.
public static class GlobalVar
{
private static JobsDictionary jobsDictionary = new JobsDictionary();
public static JobData Job(string jobCat)
{
JobData result;
if (jobsDictionary.TryGetValue(jobCat, out result))
return result;
//if the jobsDictionary is not initialized yet...
lock (jobsDictionary)
{
if (jobsDictionary.Count == 0)
{
//TODO: get the Data from the Database
jobsDictionary.Add("earnings", "EarningsWhispers", "http://www.earningswhispers.com/stocks.asp?symbol={0}");
jobsDictionary.Add("stock", "YahooStock", "http://finance.yahoo.com/q?s={0}");
jobsDictionary.Add("functions", "Functions", null);
}
return jobsDictionary[jobCat];
}
}
}
If you are populating the collection once, you don't need any locking at all, since a Dictionary is thread-safe when it is only read from. If you want prevent multiple threads from initializing multiple times you can use a double-checked lock during initalization, like this:
static readonly object syncRoot = new object();
static Dictionary<string, JobData> cache;
static void Initialize()
{
if (cache == null)
{
lock (syncRoot)
{
if (cache == null)
{
cache = LoadFromDatabase();
}
}
}
}
Instead of allowing every thread to access the dictionary, hide it behind a facade that only exposes the operations you really need. This makes it much easier to reason about thread-safety. For instance:
public class JobDataCache : IJobData
{
readonly object syncRoot = new object();
Dictionary<string, JobData> cache;
public void AddJob(string key, JobData data)
{
lock (this.syncRoot)
{
cache[key] = data;
}
}
}
Trying to prevent locking without having measured that locking actually has a too big impact on performance is bad. Prevent doing that. Often using a simple lock statement is much simpler than writing lock-free code. There is a nasty problem with concurrency bugs compared to normal software bugs. They are very hard to reproduce and very hard to track down. If you can, prevent writing concurrency bugs. You can do this by writing the simplest code you can, even if it is slower. If it proves to be too slow, you can always optimize.
If you want to write lock-free code anyway, try using immutable data structures, or prevent changing existing data. This is one trick I used when writing the Simple Injector (a reusable library). In this framework, I never update the internal dictionary, but always completely replace it with a new one. The dictionary itself is therefore never changed, the reference to that instance is just replaced with a completely new dictionary. This prevents you from having to do locks completely. However, you must realize that it is possible to loose updates. In other words, when multiple threads are updating that dictionary, one can loose its changes, simply because each thread creates a new copy of that dictionary and adds its own value too its own copy, before making that reference public to other threads.
In other words, you can only use this method when external callers only read (and you can recover from lost changes, for instance by querying the database again).
UPDATE
Your updated version is still not thread-safe, because of the reasons I explained on #ili's answer. The following will do the trick:
public static class GlobalVar
{
private static readonly object syncRoot = new object();
private static JobsDictionary jobsDictionary = null;
public static JobData Job(string jobCat)
{
Initialize();
return jobsDictionary[jobCat];
}
private void Initialize()
{
// Double-checked lock.
if (jobsDictionary == null)
{
lock (syncRoot)
{
if (jobsDictionary == null)
{
jobsDictionary = CreateJobsDictionary();
}
}
}
}
private static JobsDictionary CreateJobsDictionary()
{
var jobs = new JobsDictionary();
//TODO: get the Data from the Database
jobs.Add("earnings", "EarningsWhispers", "http://...");
jobs.Add("stock", "YahooStock", "http://...");
jobs.Add("functions", "Functions", null);
return jobs;
}
}
You can also use the static constructor, which would prevent you from having to write the double checked lock yourself. However, it is dangarous to call the database inside a static constructor, because a static constructor will only run once and when it fails, the complete type will be unusable for as long as the AppDomain lives. In other words your application must be restarted when this happens.
UPDATE 2:
You can also use .NET 4.0's Lazy<T>, which is safer than a double checked lock, since it is easier to implement (and easier to implement correctly) and is is also thread-safe on processor architectures with weak memory models (weaker than x86 such as ARM):
static Lazy<Dictionary<string, JobData>> cache =
new Lazy<Dictionary<string, JobData>>(() => LoadFromDatabase());
1) Use singleton patern to have one instance (one of the ways is to use static class as you have done)
2) To make anything thread safe you should use lock or it's analog. If you are afraids of unnessessary locks do like this:
public object GetValue(object key)
{
object result;
if(_dictionary.TryGetValue(key, out result)
return result;
lock(_dictionary)
{
if(_dictionary.TryGetValue(key, out result)
return result;
//some get data code
_dictionary[key]=result;
return result;
}
}

Is This a Good Design for Creating Thread-Safe Classes in C#?

Often, when I want a class which is thread-safe, I do something like the following:
public class ThreadSafeClass
{
private readonly object theLock = new object();
private double propertyA;
public double PropertyA
{
get
{
lock (theLock)
{
return propertyA;
}
}
set
{
lock (theLock)
{
propertyA = value;
}
}
}
private double propertyB;
public double PropertyB
{
get
{
lock (theLock)
{
return propertyB;
}
}
set
{
lock (theLock)
{
propertyB = value;
}
}
}
public void SomeMethod()
{
lock (theLock)
{
PropertyA = 2.0 * PropertyB;
}
}
}
It works, but it is very verbose. Sometimes I even create a lock object for each method and property creating more verbosity and complexity.
I know that it is also possible to lock classes using the Synchronization attribute but I'm not sure how well that scales -- as I often expect to have hundreds of thousands, if not millions, of instances of thread-safe objects. This approach would create a synchronization context for every instance of the class, and requires the class to be derived from ContextBoundObject and therefore could not be derived from anything else -- since C# doesn't allow for multiple inheritance -- which is a show stopper in many cases.
Edit: As several of the responders have emphasized, there is no "silver bullet" thread-safe class design. I'm just trying to understand if the pattern I'm using is one of the good solutions. Of course the best solution in any particular situation is problem dependent. Several of the answers below contain alternative designs which should be considered.
Edit: Moreover, there is more than one definition of thread safety. For example, in my implementation above, the following code would NOT be thread-safe:
var myObject = new ThreadSafeClass();
myObject.PropertyA++; // NOT thread-safe
So, does the class definition above represent a good approach? If not, what would you recommend for a design with similar behavior which would be thread-safe for a similar set of uses?
There is no "one-size-fits-all" solution to the multi-threading problem. Do some research on creating immutable classes and learn about the different synchronization primitives.
This is an example of a semi-immutable or the-programmers-immutable class .
public class ThreadSafeClass
{
public double A { get; private set; }
public double B { get; private set; }
public double C { get; private set; }
public ThreadSafeClass(double a, double b, double c)
{
A = a;
B = b;
C = c;
}
public ThreadSafeClass RecalculateA()
{
return new ThreadSafeClass(2.0 * B, B, C);
}
}
This example moves your synchronization code into another class and serializes access to an instance. In reality, you don't really want more than one thread operating on an object at any given time.
public class ThreadSafeClass
{
public double PropertyA { get; set; }
public double PropertyB { get; set; }
public double PropertyC { get; set; }
private ThreadSafeClass()
{
}
public void ModifyClass()
{
// do stuff
}
public class Synchronizer
{
private ThreadSafeClass instance = new ThreadSafeClass();
private readonly object locker = new object();
public void Execute(Action<ThreadSafeClass> action)
{
lock (locker)
{
action(instance);
}
}
public T Execute<T>(Func<ThreadSafeClass, T> func)
{
lock (locker)
{
return func(instance);
}
}
}
}
Here is a quick example of how you would use it. It may seem a little clunky but it allows you to execute many actions on the instance in one go.
var syn = new ThreadSafeClass.Synchronizer();
syn.Execute(inst => {
inst.PropertyA = 2.0;
inst.PropertyB = 2.0;
inst.PropertyC = 2.0;
});
var a = syn.Execute<double>(inst => {
return inst.PropertyA + inst.PropertyB;
});
I know this might sound like an smart a** answer but ... the BEST way to develop threadsafe classes is to actually know about multithreading, about its implications, its intricacies and what does it implies. There's no silver bullet.
First you need a good reason to use it. Threads are a tool, you don't want to hit everything with your new found hammer.
Secondly, learn about the problems of multithreading... deadlocks, race conditions, starvation and so on
Third, make sure is worth it. I'm talking about benefit/cost.
Finally... be prepared to heavy debugging. Debugging multithreaded code is much more difficult than standard old sequential code. Learn some techniques about how to do that.
Seriously... don't try to multithread (in production scenarios I mean) until you know what you're getting yourself into... It can be a huge mistake.
Edit: You should of course know the synchronization primitives of both the operating system and your language of choice (C# under Windows in this case, I guess).
I'm sorry I'm not giving just the code to just make a class threadsafe. That's because it does not exist. A completely threadsafe class will probably just be slower than just avoiding threads and will probably act as a bottleneck to whatever you're doing... effectively undoing whatever you thing you're achieving by using threads.
Bear in mind that the term "thread safe" is not specific; what you're doing here would be more accurately referred to as "synchronization" through the use of a Monitor lock.
That said, the verbosity around synchronized code is pretty much unavoidable. You could cut down on some of the whitespace in your example by turning things like this:
lock (theLock)
{
propertyB = value;
}
into this:
lock (theLock) propertyB = value;
As to whether or not this is the right approach for you we really need more information. Synchronization is just one approach to "thread safety"; immutable objects, semaphores, etc. are all different mechanisms that fit different use-cases. For the simple example you provide (where it looks like you're trying to ensure the atomicity of a get or set operation), then it looks like you've done the right things, but if your code is intended to be more of an illustration than an example then things may not be as simple.
Since no else seems to be doing it, here is some analysis on your specific design.
Want to read any single property? Threadsafe
Want to update to any single property? Threadsafe
Want to read a single property and then update it based on its original value? Not Threadsafe
Thread 2 could update the value between thread 1's read and update.
Want to update two related properties at the same time? Not Threadsafe
You could end up with Property A having thread 1's value and Property B having thread 2's value.
Thread 1 Update A
Thread 2 Update A
Thread 1 Update B
Thread 2 Update B
Want to read two related properties at the same time? Not Threadsafe
Again, you could be interrupted between the first and second read.
I could continue, but you get the idea. Threadsafety is purely based on how you plan to access the objects and what promises you need to make.
You may find the Interlocked class helpful. It contains several atomic operations.
One thing you could do that could help you avoid the extra code is use something like PostSharp to automatically inject those lock statements into your code, even if you had hundreds of them. All you'd need is one attribute attached to the class, and the attribute's implementation which would add the extra locking variables.
As per my comment above - it gets a little hairier if you want simultaneous readers allowed but only one writer allowed. Note, if you have .NET 3.5, use ReaderWriterLockSlim rather than ReaderWriterLock for this type of pattern.
public class ThreadSafeClass
{
private readonly ReaderWriterLock theLock = new ReaderWriterLock();
private double propertyA;
public double PropertyA
{
get
{
theLock.AcquireReaderLock(Timeout.Infinite);
try
{
return propertyA;
}
finally
{
theLock.ReleaseReaderLock();
}
}
set
{
theLock.AcquireWriterLock(Timeout.Infinite);
try
{
propertyA = value;
}
finally
{
theLock.ReleaseWriterLock();
}
}
}
private double propertyB;
public double PropertyB
{
get
{
theLock.AcquireReaderLock(Timeout.Infinite);
try
{
return propertyB;
}
finally
{
theLock.ReleaseReaderLock();
}
}
set
{
theLock.AcquireWriterLock(Timeout.Infinite);
try
{
propertyB = value;
}
finally
{
theLock.ReleaseWriterLock();
}
}
}
public void SomeMethod()
{
theLock.AcquireWriterLock(Timeout.Infinite);
try
{
theLock.AcquireReaderLock(Timeout.Infinite);
try
{
PropertyA = 2.0 * PropertyB;
}
finally
{
theLock.ReleaseReaderLock();
}
}
finally
{
theLock.ReleaseWriterLock();
}
}
}

Categories