Introduction
After writing C# threaded applications for a few years, I started to get bored of writing the synchronization code for each property I wanted to make thread-safe. If you want your class to manage the locking in a neat OOP encapsulated way, you must create locking variables and handle access to each property using get { }
and set { }
.
So came the idea of writing a simple wrapper class to do the dirty work for me. The Synchronized
class is my first try at this, and can hopefully be further developed and extended.
I hope this can be of interest to some, and I really do hope to get some feedback to improve and extend my work. :)
Background
The basics to make this work are the use of Generics and type casting overloading. I suggest those who are not familiar with these concepts have a look at the .NET documentation or at one of the many good articles on the web.
Using the code
A typical C# approach to making properties thread-safe would be something like:
internal class MyThreadSafeCass
{
private object PropertyLock = new object();
private int m_Property = 0;
internal int Property
{
get
{
lock (PropertyLock)
{
return m_Property;
}
}
set
{
lock (PropertyLock)
{
m_Property = value;
}
}
}
}
Now, every thread in your application can access Property
in a thread-safe way.
A class containing a few properties which must be made thread-safe would lead to writing quite some code, and decrease the overall neatness and readability.
Let's have a look at the Synchronized
wrapper class. Its definition is quite simple:
using System;
namespace STA.Threading
{
internal class Synchronized<T>
{
private object m_ValueLock;
private T m_Value;
internal T Value
{
get
{
lock (m_ValueLock)
{
return m_Value;
}
}
set
{
lock (m_ValueLock)
{
m_Value = value;
}
}
}
internal Synchronized()
{
m_ValueLock = new object();
}
internal Synchronized(T value)
{
m_ValueLock = new object();
Value = value;
}
internal Synchronized(T value, object Lock)
{
m_ValueLock = Lock;
Value = value;
}
public static implicit operator T(Synchronized<T> value)
{
return value.Value;
}
}
}
It makes use of Generics in order to be able to wrap any type, and overloads the implicit type casting to the wrapped type to allow a Synchronized
object to be used in place of the wrapped type, where possible.
Now, we can use it to modify our test class:
using STA.Threading;
internal class MyThreadSafeCass
{
internal Synchronized<int> Property = new Synchronized<int>(0);
}
Simple and neat.
What it does and what it does not
A property wrapped in a Synchronized
class can be used in many common situations without needing any special syntax, but there are some exceptions. Here is a brief summary of its most common uses:
using STA.Threading;
...
Synchronized<int> s1 = new Synchronized(10);
Synchronized<int> s2 = new Synchronized();
s2 = 20;
s2.Value = 20;
int sum = s1 + s2:
s1++;
s1.Value++;
int TestFunction(int Value)
{
return Value * 10;
}
int ret = TestFunction(s1);
int.TryParse("50", out s1);
int.TryParse("50", out s1.Value);
int tmp;
int.TryParse("50", out tmp);
s1.Value = tmp;
...
The biggest limitation with this approach is it's no good with properties of complex types. For example, a Synchronized<DataDet>
property will not render calls to the wrapped DataSet
's properties or thread-safe methods.
Points of interest
I found the approach to implicit and explicit type casting overloading in C# particularly nice and efficient. Before this project, I only had experience with unary and binary operators overloading, and when I first started looking around in the documentation for some hint as to how to implement my idea, I doubted there would be such an easy solution.
History
- 2009-02-21 - V1.0 - First release.