2014-05-14 101 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