简体   繁体   English

F#同步访问列表

[英]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. A )安全的方法是将此变量包装到代理中并通过序列化消息访问它,我们甚至不需要mutable修饰符。 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: B )AFAIK引用赋值在.NET中是原子的,因此在一个发布者和所有消费者之间进行简单的赋值就足够了:

Publisher: data <- newItem :: data 发布者: data <- newItem :: data

Consumer: data |> process 消费者: 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) 发布者: lock monitor (fun () -> data <- newItem::data)

Am I right with my assumptions? 我的假设是对的吗? Which approach is preferred and would be more idiomatic for F#? 哪种方法更受欢迎,对于F#来说更为惯用? Any better options? 有更好的选择吗?

You could use Interlocked.CompareExchange to handle publishing without explicitly locking: 您可以使用Interlocked.CompareExchange来处理发布而不显式锁定:

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. 如果您确定希望消费者始终拥有最新数据,则ReaderWriterLockSlim将允许您完全同步数据,而不会强制每次调用时阻止读取。

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 your shared data is immutable you can do this safely: 如果您的共享数据是不可变的,您可以安全地执行此操作:

let monitor = new System.Object()
let data : int list ref = ref List.empty

let modData (modFun : int list -> int list) =
   lock (monitor) (fun _ -> data := modFun !data)

You can read the data when ever you like without locking. 您可以随时阅读数据而无需锁定。 Data is immutable so it cannot be "between states". 数据是不可变的,因此它不能“在状态之间”。 If you need to read and write atomically, you can do it in the "modFun". 如果你需要原子地读写,你可以在“modFun”中完成。

Usage example: 用法示例:

modData (function |(head::tail) -> tail |_ -> List.Empty) //Remove one 
modData (fun thelist -> 1::thelist) //Add number 1 to head

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM