Sasha Goldshtein is a Senior Consultant for Sela Group, an Israeli company specializing in training, consulting and outsourcing to local and international customers.Sasha's work is divided across these three primary disciplines. He consults for clients on architecture, development, debugging and performance issues; he actively develops code using the latest bits of technology from Microsoft; and he conducts training classes on a variety of topics, from Windows Internals to .NET Performance. You can read more about Sasha's work and his latest ventures at his blog: http://blogs.microsoft.co.il/blogs/sasha. Sasha writes from Herzliya, Israel. Sasha is a DZone MVB and is not an employee of DZone and has posted 204 posts at DZone. You can read more from them at their website. View Full User Profile

Practical Concurrency Patterns: Immutability (Freezables)

08.18.2008
| 5547 views |
  • submit to reddit

Another very simple pattern builds on the foundation of the Safe-Unsafe Cache pattern.  What is the easiest way to protect data from multi-threaded access and to incur the minimal performance cost while doing so?  Making it read-only!

Read-only (immutable) objects are extremely convenient to use because there’s no need to enforce thread-safety – all accesses to read-only objects are thread-safe.  Additionally, immutable objects decrease complexity – it’s impossible for one part of the code to modify an object that is used by another part of the code (even if not concurrently from different threads).

Due to these benefits, some types are immutable by definition – for example, the .NET string is immutable (unless you resort to ugly pointer-manipulation tricks).  Other types can be made immutable at some point after they have been used – for example, the SafeUnsafeCache<T> can be locked to and switched to a read-only mode.  WPF has a similar feature called freezable objects – objects derived from Freezable can be frozen so that they can be safely accessed and referenced from multiple threads and points in code.

Mutating an immutable object is an illusion: It always results in the creation of a new object that has some properties similar to the prototype.  Eric Lippert’s series on immutable types in C# features the process of implementing an immutable type.  The following is an excruciatingly simple example of an object that becomes immutable at some point (and guarantees thread safety from that moment on):

public sealed class FreezableEmployee
{

private string _name;
private int _salary;
private volatile bool _isFrozen;

public FreezableEmployee(string name, int salary)
{
_name = name;
_salary = salary;
}

public void Freeze()
{
_isFrozen = true;
}

public string Name
{
get { return _name; }
set
{
CheckIfFrozen();
_name = value;
}
}

public int Salary
{
get { return _salary; }
set
{
CheckIfFrozen();
_salary = value;
}
}

private void CheckIfFrozen()
{
if (_isFrozen)
throw new InvalidOperationException("Attempted to modify a frozen instance");
}
}

Yes, it’s amazingly dumb but hey – patterns are supposed to be simple!  It is their simplicity that makes them usable.

While we’re on the topic of immutable objects, there’s another little corner to which we should attend.  Generally speaking, constructing a read-only object and giving it away to other code is slightly dangerous in and of itself, because constructing an object means writing to it.  If a partially constructed object is given away to other code, then that other code might observe changes to the object even though it is considered to be immutable.

Fortunately, under the memory model enforced by CLR 2.0, all writes are considered volatile writes, which ensures that reads cannot drift past them.  This means that assigning a constructed object to a reference that can be used by calling code occurs after the object is fully constructed and all writes to its internal fields are visible.  (Curiously enough, the JVM memory model provides this guarantee only for final fields.)

References
Published at DZone with permission of Sasha Goldshtein, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)