Skip to content

Instantly share code, notes, and snippets.

@duanenewman
Created February 19, 2019 17:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save duanenewman/6bf027fae3aab5aa85342eb34c94d9d4 to your computer and use it in GitHub Desktop.
Save duanenewman/6bf027fae3aab5aa85342eb34c94d9d4 to your computer and use it in GitHub Desktop.
Concurrent Reads and Locked Writes with In-Memory Objects
static Random rnd = new Random();
void Main()
{
NoLocking();
UsingReadUpdateLocker();
UsingInstanceReadUpdateLocker();
UsingInstanceReaderWriterLock();
}
void NoLocking()
{
try
{
var datas = BuildList();
var tasks = CreateTasks(
() => GetCount(datas, true),
() => Update(datas, true));
Task.WaitAll(tasks.Where(t => { t.Start(); return true; }).ToArray());
$"NoLocking passed without exception".Dump();
}
catch (AggregateException ex)
{
$"NoLocking exception: {Environment.NewLine}{string.Join(Environment.NewLine, ex.InnerExceptions.Select(ie => ie.Message))}".Dump();
}
}
void UsingReadUpdateLocker()
{
var locker = new ReadUpdateLocker();
var datas = BuildList();
var tasks = CreateTasks(
() => locker.DoRead(() => GetCount(datas, true)),
() => locker.DoUpdate(() => Update(datas, true)));
Task.WaitAll(tasks.Where(t => { t.Start(); return true; }).ToArray());
}
void UsingInstanceReadUpdateLocker()
{
var locker = new InstanceReadUpdateLocker<List<Data>>(BuildList());
var tasks = CreateTasks(
() => locker.DoRead(d => GetCount(d, true)),
() => locker.DoUpdate(d => Update(d, true)));
Task.WaitAll(tasks.Where(t => { t.Start(); return true; }).ToArray());
}
void UsingInstanceReaderWriterLock()
{
var locker = new InstanceReaderWriterLock<List<Data>>(BuildList());
var tasks = CreateTasks(
() => locker.DoRead(d => GetCount(d, true)),
() => locker.DoUpdate(d => Update(d, true)));
Task.WaitAll(tasks.Where(t => { t.Start(); return true; }).ToArray());
}
List<Task> CreateTasks(Action readAction, Action updateAction)
{
var tasks = new List<Task>();
for (int i = 0; i < 100; i++)
{
Task task;
if (rnd.NextDouble() < 0.8)
{
task = new Task(readAction);
}
else
{
task = new Task(updateAction);
}
tasks.Add(task);
}
return tasks;
}
List<Data> BuildList()
{
var datas = new List<Data>();
for (int i = 0; i < 100; i++)
{
Update(datas, true);
}
return datas;
}
List<Data> GetCount(List<Data> datas, bool silent = false)
{
Thread.Sleep(1);
var value = rnd.Next(10);
var matchingDatas = datas.Where(d => d.Value == value);
if (!silent) Console.WriteLine($"{matchingDatas.Count()} with value {value}");
return matchingDatas.ToList();
}
void Update(List<Data> datas, bool silent = false)
{
Thread.Sleep(1);
var data =new Data() { Value = rnd.Next(10) };
datas.Add(data);
if (!silent) Console.WriteLine($"Added {data.Value}");
}
class Data
{
public int Value { get; set; }
}
public class ReadUpdateLocker
{
private readonly object UpdateLock = new object();
private readonly object CounterLock = new object();
private int ReadCount = 0;
private void IncrementReadCounter()
{
lock (UpdateLock)
{
Interlocked.Increment(ref ReadCount);
}
}
private void DecrementReadCounter()
{
Interlocked.Decrement(ref ReadCount);
}
public void DoRead(Action readAction)
{
try
{
IncrementReadCounter();
readAction.Invoke();
}
finally
{
DecrementReadCounter();
}
}
public void DoUpdate(Action updateAction)
{
lock (UpdateLock)
{
//wait for counter to hit 0;
while (ReadCount > 0)
{
Thread.Sleep(0);
}
updateAction.Invoke();
}
}
}
public class InstanceReadUpdateLocker<T>
{
private ReadUpdateLocker locker = new ReadUpdateLocker();
private T Data { get; set; }
public InstanceReadUpdateLocker(T data)
{
Data = data;
}
public T DoRead(Func<T, T> readAction)
{
var result = default(T);
locker.DoRead (() => result = readAction(Data));
return result;
}
public void DoUpdate(Action<T> updateAction)
{
locker.DoUpdate (() => updateAction(Data));
}
}
public class InstanceReaderWriterLock<T>
{
private ReaderWriterLock locker = new ReaderWriterLock();
private T Data { get; set; }
private int timeout = 500;
public InstanceReaderWriterLock(T data)
{
Data = data;
}
public T DoRead(Func<T, T> readAction)
{
try
{
locker.AcquireReaderLock(timeout);
return readAction.Invoke(Data);
}
finally
{
locker.ReleaseReaderLock();
}
}
public void DoUpdate(Action<T> updateAction)
{
try
{
locker.AcquireWriterLock(timeout);
updateAction.Invoke(Data);
}
finally
{
locker.ReleaseWriterLock();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment