In the post about Basic thread safety in .Net we looked at techniques to avoid state corruption in asynchronous code.
In this post I will briefly show how you can utilize an Immutable state object for a very brief lock so you can access immutable data to ease your synchronization of your algorithms. The source code for this can be found here.
What this post will cover
- ImmutableState object technique
What this post will not cover
- The lock statement
- What are mutable shared state issues
The general idea is to create an object, that encapsulates the state changes you want to apply and then create a new instance of it for each assignment, so that multistage transition can be handled by a single lock statement and assignment. We will look at a general example first and then how this can be abstracted for a more generic approach.
In the example I want to achieve a change to the fields of count and name simultaneously:
public class MyNotSoImmutableClas { private string _name; private int _count; private static object _stateGuard = new object(); public void Edit(int i, string name) { lock (_stateGuard) { _name = name; _count = i; } } public int GetCount() => _count; public void GetName() => _name; }
Now this is not a terribly good example, yet I think it gets the point across. To make those two statements into a single assignment operation we create an immutable state object:
public class MyImmutableState { public readonly int Count; public readonly string Name; public MyImmutableState(int i, string, name) { Count = i; Name = name; } }
Which we then use in the following class:
public class MyUserOfImmutableState { private readonly object _stateGuard = new object(); private MyImmutableState _state; public void Edit(int i, string name) { var state = new MyImmutableState(i, name); lock (_stateGuard) _state = state; } public int GetCount() { MyImmutableState state; lock (stateGuard) state = _state; return state.Count; } public int GetName() { MyImmutableState state; lock (stateGuard) state = _state; return state.Name; } }
You can see, that there are locks only for the briefest moment, when you assign the readable object or assign the new Values.
This is not optimal though, because we use Public fields, and I want to create a more generic approach to it. Public fields should be avoided, because you create a direct dependency and if you want to apply some logic to those fields in the future, you will most likely break the calling code.
So with the basic idea in mind we now create the following:
(this can be extended with as much type parameters as needed)
public class ImmutableState<T> { public T Value{get;} public ImmutableState(T value) { Value = value; } }
Usage is similar as above:
public void Edit<T>(T assignableValue) { var immutable = new ImmutableState<T>(assignableValue); lock (_stateGuard) _immutable = immutable; }
This can all be extended by applying Design patterns like Flyweight or object pooling to make the creation of the ImmutableState object much more efficient.
Summary
In this post I introduced you briefly to the idea behind the use of immutable objects for very short locks in a concurrent environment.
We looked at a plain approach and a more abstract one.
0 Comments