Akka簡介多執行緒問題《four》譯

2021-08-22 04:34:59 字數 3571 閱讀 1729

原文:

akka是一套開放原始碼庫,用於設計可伸縮的、具有彈性的系統,跨越處理器核心和網路。akka允許您專注於滿足業務需求,而不是編寫低級別**來提供可靠的行為、容錯和高效能。

許多常見做法和使用的程式設計模型並不能解決和設計現代計算機體系結構所固有的重要挑戰。為了解決那些問題,分布式系統必須應對元件崩潰不能響應,訊息丟失時沒有軌跡,網路延遲波動。這些問題經常發生在精心管理的資料中心環境中-甚至在虛擬化架構中更是如此。

為了幫助你處理這些現實,akka提供了:

akka對actor模型的使用提供了乙個抽象級別,使得編寫正確的併發、並行和分布式系統變得更容易。角色模型跨越了全部akka庫,為您提供了一種理解和使用它們的一致方式。因此,akka提供了深度的整合,您無法通過挑選庫來解決單個問題並嘗試將它們組合在一起來實現。

通過學習akka和如何使用actor模型,您將獲得一組廣泛而深入的工具,這些工具可以在統一的程式設計模型中解決困難的分布式/並行系統問題,在這個模型中,一切都緊密而有效地結合在一起。

角色模型是幾十年前由卡爾·休伊特(carlhewitt)提出的,作為在高效能網路中處理並行處理的一種方式-這種環境在當時是不可用的。今天,硬體和基礎設施能力已經趕上並超過了休伊特的設想。因此,構建具有嚴格需求的分布式系統的組織遇到了傳統的物件導向程式設計(oop)模型無法完全解決的挑戰,但它可以從角色模型中受益。

今天,角色模型不僅被認為是一種非常有效的解決方案-它已經在世界上一些最苛刻的應用程式的生產中得到了證明。為了突出角色模型解決的問題,本主題討論傳統程式設計與現代多執行緒多cpu體系結構的現實之間的不匹配:

oop的核心支柱是封裝。封裝規定物件的內部資料不能直接從外部訪問;它只能通過呼叫一組精心設計的方法來修改。該物件負責公開保護其封裝資料的不變性質的安全操作。

例如,對有序二叉樹實現的操作必須不允許違反樹的順序不變。呼叫方希望順序是完整的,並且當查詢樹以獲取特定的資料時,他們需要能夠依賴於這個約束。

當我們分析oop執行時行為時,我們有時會繪製乙個顯示方法呼叫互動的訊息序列圖。例如:

不幸的是,上面的圖表不能準確地表示執行過程中例項的生命線。實際上,乙個執行緒執行所有這些呼叫,執行發生在呼叫方法的在同乙個執行緒上。用執行執行緒更新關係圖,如下所示:

當您嘗試對多執行緒所發生的情況進行建模時,這種澄清的意義就變得清楚了。突然間,我們畫得很整齊的圖表變得不夠用了。我們可以嘗試說明訪問同乙個例項的多個執行緒:

有一段執行,兩個執行緒進入相同的方法。不幸的是,物件的封裝模型並不能保證該部分中發生了什麼。這兩個呼叫的指令可以任意的方式交織在一起,這就消除了保持變數不變的任何希望,而無需在兩個執行緒之間進行某種型別的協調。現在,想象一下這個問題由於存在許多執行緒而變得更加複雜。

解決此問題的常見方法是圍繞這些方法新增乙個鎖。雖然這可以確保在任何給定的時間最多有乙個執行緒進入該方法,但這是一種代價很高的策略:

這些現實導致了一種不成功的局面:

此外,鎖只能在本地正常工作。當涉及到跨多台計算機進行協調時,唯一的選擇是分布式鎖。不幸的是,分布式鎖的效率比本地鎖低幾個數量級,並且通常對擴充套件施加乙個硬限制。分布式鎖協議需要在多台計算機上通過網路進行多次通訊,因此延遲時間過高。

在物件導向語言中,我們通常很少考慮執行緒或線性執行路徑。我們經常設想乙個系統是乙個物件例項網路,它對方法呼叫作出反應,修改它們的內部狀態,然後通過方法呼叫相互通訊,從而推動整個應用程式狀態向前:

然而,在多執行緒分布式環境中,實際發生的事情是執行緒通過以下方法呼叫「遍歷」這個物件例項網路。因此,真正驅動執行緒執行的是:

總的來說:

現代計算機體系結構中共享記憶體的錯覺

80『-90年代的程式設計模型認為寫入變數意味著直接寫入記憶體位置(這可能模糊了區域性變數可能只存在於暫存器中)。對於現代架構,如果我們簡化了一些事情,那麼cpu就會編寫快取行而不是直接寫記憶體。大多數快取都是本地cpu核心,也就是說,乙個核心寫的都不能被另乙個核心所顯示。為了使本地更改可見於另乙個核心,因此對於另乙個執行緒來說,快取行需要被傳送到其他核心快取。

在jvm上,我們必須顯式地表示通過使用volatile標記或原子包裝來跨執行緒共享的記憶體位置。否則,我們只能在鎖定的部分訪問它們。為什麼我們不把所有變數標記為volatile?因為傳送快取記憶體線跨越核心是乙個非常昂貴的操作!這樣做會隱式地將涉及額外工作所涉及的核心拖放,從而導致快取一致性協議(協議cpu用於傳輸主記憶體和其他cpu之間快取線)瓶頸。結果是減速幅度。

即使對於了解這種情況的開發人員來說,要確定哪些記憶體位置應該標記為volatile,或者使用哪些原子結構是一種黑暗藝術。

總的來說:

call stacks的錯覺

今天,我們經常用call stacks來做理所當然的事。但是,它們是在乙個並行程式設計並不重要,多cpu系統並不常見的時代發明的。call stacks不會跨執行緒,不模擬非同步呼叫鏈。

當執行緒打算將任務委託到「後台」時,會出現問題。實際上,這實際上意味著委託另乙個執行緒。這不能是簡單的方法/函式呼叫,因為呼叫對執行緒是嚴格的本地呼叫。通常發生的是,呼叫者將物件放在乙個由工作執行緒共享的記憶體位置(「被呼叫者」),反過來,它會在某些事件迴圈中重新選擇它。這樣可以讓呼叫方執行緒繼續執行並執行其他任務。

第乙個問題是,如何才能通知「caller」完成任務?但是當任務與異常失敗時會出現更嚴重的問題。異常傳播到**?它可能將傳播到工作執行緒的異常處理程式,完全忽略實際的呼叫方是誰:

這是乙個嚴重的問題,工作執行緒如何處理這種情況?它很可能無法解決這個問題,因為它通常忽略了失敗任務的目的。需要通知「呼叫方」執行緒,但是沒有乙個呼叫堆疊以異常解除。故障通知只能通過側通道來完成,例如,在呼叫「呼叫方」執行緒時,將錯誤**放在另乙個位置上,否則會預期結果一旦就緒。如果此通知未到位,「呼叫方」將永遠不會通知失敗,任務丟失!這與網路系統工作類似,因為在沒有任何通知的情況下,訊息/請求可以丟失/失敗。

當事情發生錯誤時,這種壞情況會變得更糟,而乙個由執行緒支援的遇到了乙個bug,最終會出現無法恢復的狀況。例如,由bug引發的內部異常,將其拋到root執行緒,並使執行緒關閉。這立即引發了乙個問題:誰應該重新啟動執行緒託管服務的正常操作,以及如何恢復到乙個已知的狀態?乍一看,這似乎是可以控制的,但是我們突然面臨乙個新的、意想不到的現象:執行緒當前正在執行的實際任務不再位於任務從(通常是佇列)中的共享記憶體位置。事實上,由於異常到達頂部,解除所有呼叫堆疊,任務狀態完全丟失!儘管這是本地通訊,但沒有聯網(預計訊息損失將被預期),我們已經失去了乙個訊息。

總結:接下來,讓我們看看如何使用角色模型來克服這些挑戰。

多執行緒 執行緒簡介

1.什麼是執行緒?什麼是程序?執行緒 thread 執行緒是作業系統所能執行排程的最小單位。他被包含在程序之中,是程序的實際運作單位,也就是程式執行流的最小單位。乙個標準的執行緒由 執行緒id pc 當前指令指標 暫存器集合 堆疊組成。也被稱為輕量級的程序。乙個執行緒可以建立和撤銷另乙個執行緒,同乙...

多執行緒簡介

多執行緒指在單個程式中可以同時執行多個不同的執行緒執行不同的任務。多執行緒程式設計的目的,就是 最大限度地利用 cpu資源 當某一線程的處理不需要占用 cpu而只和 io等資源打交道時,讓需要占用 cpu的其他執行緒有機會獲得 cpu資源。從根本上說,這就是多執行緒程式設計的最終目的。乙個程式實現多...

多執行緒簡介

多執行緒是c 中常用的一種技術,可以充分利用多核cpu的計算潛能 以下以相關概念介紹多執行緒的使用 執行緒 thread 與程序 process 均為音譯詞,程序本質上就是廣義上理解的程式,而程序則是乙個程式內部的一種平行計算方法 最常見的,在ros中,node與node之間的關係,就是程序 而在乙...