Let's say I have a list of integers in the system:
let mutable data: int list = [1; 2; 3; 4; 5]
which is updated (by adding an element) by relatively few producers and consumed by lots of consumers.
Note: it's ok if consumers receive slightly outdated data.
What would be a proper way to synchronize access to this variable?
A) The safe approach would be to wrap this variable into agent and access it via serialized messages, we wouldn't even need mutable
modifier here. But this approach seems to be sub-optimal, because it would unnecessarily make all read accesses synchronized.
B) AFAIK reference assignment is atomic in .NET, so between one publisher and all consumers a simple assignment should suffice:
Publisher: data <- newItem :: data
Consumer: data |> process
So just simple locking between publishers would be enough to wrap up this workflow?
let monitor = object()
Publisher: lock monitor (fun () -> data <- newItem::data)
Am I right with my assumptions? Which approach is preferred and would be more idiomatic for F#? Any better options?
You could use Interlocked.CompareExchange to handle publishing without explicitly locking:
let mutable data = [1;2;3;4;5]
let newValue = 0
// To publish:
let mutable tmp = data;
while not(tmp.Equals(Interlocked.CompareExchange(&data, newValue::data, tmp))) do
tmp <- data
This would likely provide a small benefit if you have synchronized writers.
If you decide you do want consumers to always have the latest data, a ReaderWriterLockSlim
would allow you to completely synchronize the data without forcing reads to block on each call.
That could look something like:
let mutable data = [1;2;3;4;5]
let rwl = ReaderWriterLockSlim()
let newValue = 0
// To publish:
let publish newValue =
rwl.EnterWriteLock()
try
data <- newValue :: data
finally
rwl.ExitWriteLock()
// To read:
let readCurrent =
rwl.EnterReadLock()
try
data
finally
rwl.ExitReadLock()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With