2014-05-14 69 views
5

比方说,我在系统的整数列表:F#同步访问列表

let mutable data: int list = [1; 2; 3; 4; 5] 

内被更新(通过添加元素)的相对生产者和很多消费者的消耗。

注意:如果消费者收到稍微过时的数据,这没关系。

什么是正确的方式来同步访问这个变量?

一个)安全的方法是把这个包变量到代理,并通过序列化的消息访问它,我们甚至不需要在这里mutable修改。但是这种方法似乎是次优的,因为它会不必要地使所有读取访问同步。

)AFAIK参考分配是.NET中的原子,所以一个出版商和所有的消费者之间的一个简单的任务就足够了:

出版商:data <- newItem :: data

消费者:data |> process

所以才发布商之间的简单锁定足以结束此工作流程?

let monitor = object() 

出版商:lock monitor (fun() -> data <- newItem::data)

我说得对不对我的假设?哪种方法更受欢迎,而且对F#更具惯用性?有更好的选择吗?

+0

另一个想法是退一步,想传递,而不是共享存储器的消息方面,因而避免了对锁的需要。例如。:http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx – Mau

+0

这是选项'A'在这个问题中列出,这个用例似乎并不是非常有效。 – Grozz

回答

8

你可以使用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 

这可能会提供一个小的好处,如果你有同步的作家。

如果您决定希望消费者始终拥有最新数据,ReaderWriterLockSlim将允许您在不强制读取的情况下完全同步数据,从而阻止每次调用。

这可能看起来像:

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() 
0

如果你的共享数据是不可变的,你可以放心地做到这一点:

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) 

当过你喜欢不锁定您可以读取数据。数据是不可变的,所以它不能是“在国家之间”。如果你需要自动读写,你可以在“modFun”中完成。

用例:

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