First, I am aware of questions like this:
reference assignment is atomic so why is Interlocked.Exchange(ref Object, Object) needed?
... but I am still uncertain if I can avoid using lock(){} in my case.
In my case I have a class which represents some state and only a SINGLE thread that ever modifies that state from time to time. There are many threads that read the state though.
Do I need Interlocked.Exchange() in my case on the state object? Do I absolutely have to use lock(){}
Here is my example code reduced to a bare minimum:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MultiThreadingExample
{
class State
{
public int X { get; set; }
public string Str { get; set; }
public DateTime Current { get; set; }
}
class Example
{
State state;
CancellationTokenSource cts = new CancellationTokenSource();
Task updater;
List<Task> readers = new List<Task>();
public void Run()
{
updater = Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
// wait until we have a new state from some source
Thread.Sleep(1000);
var newState = new State() { Current = DateTime.Now, X = DateTime.Now.Millisecond, Str = DateTime.Now.ToString() };
// critical part
state = newState;
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
for (int i = 0; i < 10; i++)
{
readers.Add(Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
// critical part
var readState = state;
// use it
if (readState != null)
{
Console.WriteLine(readState.Current);
Console.WriteLine(readState.Str);
Console.WriteLine(readState.X);
}
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default));
}
}
}
class Program
{
static void Main(string[] args)
{
new Example().Run();
Console.ReadKey();
}
}
}