簡體   English   中英

使用LIFO邏輯運行的MailboxProcessor

[英]A MailboxProcessor that operates with a LIFO logic

我正在學習F#代理( MailboxProcessor )。

我正在處理一個非常常規的問題。

  • 我有一個代理( dataSource ),它是流數據的源。 數據必須由代理數組( dataProcessor )處理。 我們可以將dataProcessor視為某種跟蹤設備。
  • 數據的流入速度可能比dataProcessor可能處理其輸入的速度快。
  • 可以有一些延遲。 但是,我必須確保代理始終處於工作狀態,並且不會因過時的觀察而堆積

我正在探索解決此問題的方法。

一個想法是在dataSource實現堆棧 (LIFO)。 dataProcessor可用於接收和處理數據時, dataSource將發送可用的最新觀察值。 該解決方案可能有效,但由於可能需要阻止並重新激活dataProcessor因此可能會變dataProcessor復雜。 並將其狀態傳達給dataSource ,從而導致雙向通訊問題。 這個問題可能歸結為消費者-生產者問題中blocking queue ,但是我不確定。

第二個想法是讓dataProcessor負責消息排序。 在這種體系結構中, dataSource只會將更新發布到dataProcessor的隊列中。 dataProcessor將使用“ Scan來獲取隊列中可用的最新數據。 這可能是要走的路。 但是,我不確定在當前的MailboxProcessor設計中是否可以清除消息隊列,刪除較舊的過時消息。 此外, 在這里 ,寫道:

不幸的是,當前版本的F#中的TryScan函數以兩種方式被破壞。 首先,重點是指定一個超時,但是實現實際上並沒有兌現它。 具體來說,無關的消息會重置計時器。 其次,與其他“掃描”功能一樣,將在鎖定下檢查消息隊列,該鎖定可防止在掃描期間(可能是任意長時間)發布任何其他線程。 因此,TryScan函數本身傾向於鎖定並發系統,甚至可能引入死鎖,因為調用者的代碼是在鎖內求值的(例如,當鎖下的代碼阻塞等待時,從函數參數到Scan或TryScan的發布會死鎖代理。獲取它已經在下面的鎖)。

使最新的觀測結果反彈可能是一個問題。 這篇文章的作者@Jon Harrop建議

我設法圍繞它進行了架構,並且最終的架構實際上更好。 本質上,我渴望使用我自己的本地隊列Receive所有消息並進行過濾。

這個想法肯定值得探索,但是在開始使用代碼之前,我將歡迎一些有關如何構建解決方案的意見。

謝謝。

聽起來您可能需要使用破壞性的郵箱處理器掃描版本,我在您可能感興趣的博客系列中使用TPL Dataflow實現了此功能。

我的博客目前正在維護中,但我可以將您指向Markdown格式的帖子。

第1部分
第2部分
第三部分

您也可以在github上查看代碼

我還在潛伏的恐怖帖子中寫了關於掃描的問題

希望有幫助...

tl; dr我會嘗試:從FSharp.Actor或Zach Bray的博客文章中獲取郵箱實現,將ConcurrentQueue替換為ConcurrentStack(並添加一些有限容量邏輯),然后使用此已更改的代理作為調度程序,將消息從數據源傳遞到實現為普通MBP或Actor的dataProcessor。

tl; dr2如果worker是一種稀缺且緩慢的資源,並且我們需要處理一條消息,該消息是worker准備就緒時的最新消息,那么所有這些消息都歸結為具有堆棧而不是隊列(具有一定界限)的代理容量邏輯)以及工作人員的BlockingQueue。 分派器使准備就緒的工作人員出隊,然后從堆棧中彈出一條消息,然后將此消息發送給該工作人員。 作業完成后,工作者准備就緒時let! msg = inbox.Receive()自己排入隊列(例如,在let! msg = inbox.Receive() )。 然后,分派器使用者線程將阻塞,直到任何工作程序就緒為止,而生產者線程將使有界堆棧保持更新。 (有界堆棧可以用數組+偏移+鎖中的大小來完成,下面太復雜了)

細節

MailBoxProcessor設計為只有一個使用者。 這甚至在MBP的源代碼注釋這里 (搜索單詞“龍吟” :))

如果將數據發布到MBP,則只有一個線程可以從內部隊列或堆棧中獲取數據。 在您的特定用例中,我將直接使用ConcurrentStack或更好地包裝到BlockingCollection中

  • 這將允許許多並發消費者
  • 它非常快速且線程安全
  • BlockingCollection具有BoundedCapacity屬性,該屬性使您可以限制集合的大小。 它會引發Add ,但是您可以捕獲它或使用TryAdd 如果A是主堆棧,B是備用堆棧,則將TryAdd Add到A,如果是false則Add到B,然后將它們與Interlocked.Exchange交換,然后在A中處理所需的消息,將其清除,制作一個新的備用-或使用三個堆棧如果處理A的時間可能長於B的時間,則處理B可能再次變滿; 這樣,您不會阻塞也不會丟失任何消息,但是可以丟棄不需要的消息是一種受控方式。

BlockingCollection具有AddToAny / TakeFromAny之類的方法,這些方法可用於BlockingCollections的數組。 這可能會有所幫助,例如:

  • dataSource使用ConcurrentStack實現(BCCS)生成消息到BlockingCollection
  • 另一個線程使用來自BCCS的消息,並將其發送到處理BCCS的數組。 您說有很多數據。 您可能會犧牲一個線程來無限期地阻止和分發消息
  • 每個處理代理都有自己的BCCS或實現為調度程序向其發布消息的代理/演員/ MBP。 在您的情況下,您只需要發送一條消息給一個processorAgent,因此您可以將處理代理存儲在循環緩沖區中,以便始終將消息發送給最近最少使用的處理器。

像這樣:

            (data stream produces 'T)
                |
            [dispatcher's BCSC]
                |
            (a dispatcher thread consumes 'T  and pushes to processors, manages capacity of BCCS and LRU queue)
                 |                               |
            [processor1's BCCS/Actor/MBP] ... [processorN's BCCS/Actor/MBP]
                 |                               |
               (process)                         (process)

代替ConcurrentStack,您可能想了解堆數據結構 如果您需要消息的某些屬性(例如時間戳)而不是消息到達堆棧的順序(例如,如果傳輸和到達順序<>創建順序可能存在延遲),則需要獲取最新消息,則可以獲取最新消息通過使用堆消息。

如果您仍然需要Agents語義/ API,則除了閱讀Dave的鏈接之外,還可以閱讀其他資源,並以某種方式對多個並發使用者采用實現:

  • Zach Bray的一篇有趣的文章 ,介紹了Actor的高效實現。 在那里,您確實需要替換(在注釋中// Might want to schedule this call on another thread. )該行由行async { execute true } |> Async.Start或類似的行execute true ,因為否則會產生線程線程-不利於單個快速生產者。 但是,對於如上所述的調度員,這正是需要的。

  • FSharp.Actor (又名Fakka開發分支和FSharp MPB源代碼(上面的第一個鏈接)對於實現細節可能非常有用。 FSharp.Actors庫已經凍結了幾個月,但在dev分支中有一些活動。

  • 在這種情況下,不應錯過有關 Google網上論壇中Fakka的討論

我有一個類似的用例,在過去的兩天中,我研究了可以在F#Agents / Actor上找到的所有內容。 這個答案對我來說是一種嘗試嘗試這些想法的TODO,其中一半是在撰寫過程中誕生的。

最簡單的解決方案是在收件箱到達時貪婪地吃掉收件箱中的所有郵件,並丟棄除最新郵件以外的所有郵件。 使用TryReceive輕松完成:

let rec readLatestLoop oldMsg =
  async { let! newMsg = inbox.TryReceive 0
          match newMsg with
          | None -> oldMsg
          | Some newMsg -> return! readLatestLoop newMsg }
let readLatest() =
  async { let! msg = inbox.Receive()
          return! readLatestLoop msg }

遇到相同的問題時,我設計了一個更復雜,更有效的解決方案,稱為可取消流,並在此處的F#Journal文章中進行了介紹 想法是開始處理消息,如果消息被取代,則取消該處理。 如果正在進行大量處理,這將顯着提高並發性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM