Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# synchronized access to list

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?

like image 260
Grozz Avatar asked May 14 '14 18:05

Grozz


1 Answers

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()
like image 53
Reed Copsey Avatar answered Oct 22 '22 23:10

Reed Copsey