Actor模式——Python實現(xiàn)及應用

“并發(fā)是一種解耦策略” ???? —— Robert C.Martin

何為Actor模式呢?

引用維基百科的解釋 Actor Model

在計算機科學中,參與者模式(Actor model)是一種并發(fā)運算上的模型。“參與者”是一種程序上的抽象概念,被視為并發(fā)運算的基本單元:當一個參與者接收到一則消息,它可以做出一些決策、創(chuàng)建更多的參與者、發(fā)送更多的消息、決定要如何回答接下來的消息。

這段話很全面,但也需要我們花時間來消化與理解。不妨先試著閱讀以下這段Python代碼實現(xiàn),再分析理解。

from queue import Queue
from threading import Thread


class ActorExit(Exception):
    pass


class Actor(object):
    def __init__(self):
        self._thread = Thread()
        self._queue = Queue()

    def send(self, msg):
        self._queue.put(msg)

    def receive(self):
        msg = self._queue.get()
        if msg is ActorExit:
            raise ActorExit()
        return msg

    def stop(self):
        if self._thread.is_alive():
            self.send(ActorExit)

    def start(self):
        if not self._thread.is_alive():
            self._thread = Thread(target=self._bootstrap)
            self._thread.start()

    def _bootstrap(self):
        try:
            self.run()
        except ActorExit:
            pass

    def run(self):
        while True:
            msg = self.receive()
            print(msg)


# Sample Actor
class PrintActor(Actor):
    def run(self):
        while True:
            msg = self.receive()
            print("Got:", msg)


if __name__ == '__main__':
    p = PrintActor()
    p.start()
    p.send("Hello")
    p.send("World")
    p.stop()

這里我們定義了Actor模式的基類Actor和其子類實現(xiàn)PrintActor。

Actor對象是一個計算單元,所以每個actor都需要包含了一個Thread對象,用以執(zhí)行給定的消息處理操作。同時actor對象也包含了一個Queue對象,實現(xiàn)了異步調用(在send消息后能立刻返回控制權)。
在啟動actor之后,線程對象會執(zhí)行引導函數(shù)_bootstrap,進而執(zhí)行run函數(shù)。一般來說,在run函數(shù)里,我們會通過receive函數(shù)來讀取之前通過send函數(shù)發(fā)送來的消息,然后根據給定的邏輯處理消息。特別地,receive函數(shù)會拋出ActorExit異常,而ActorExit異常正是我們作為停止線程的哨兵值。

Actor model 作為一種并發(fā)運算模型,其作用在于將 目的(做什么)和時機(何時做)解耦,以優(yōu)化應用程序的吞吐量和結構。例如用戶想要打印一段信息,那么他需要去調用對應的PrintActor的send函數(shù)(目的),然后程序會立刻返回,繼續(xù)執(zhí)行操作;至于這個打印的需求何時(時機)用戶則無需關心。而這也是Actor模式吸引人之處——簡單性:用戶設計時,只需在其子類中定義好 run函數(shù)中的處理操作,使用時只需調用send函數(shù),消息就能得到對應的處理。

抽象到一個新的高度,我們可以看到Actor模式在設計上有3個并發(fā)防御原則:

  1. 單一權責:每個actor只負責處理對應類型的消息,且其中的并發(fā)代碼和其他組件的代碼是分隔開的。
  2. 限制數(shù)據成員作用域:actor只開放send接口,最大程度地減小內部成員的暴露,限制可能被共享的數(shù)據的訪問,縮減“臨界區(qū)”范圍。
  3. 線程之間獨立:每個actor線程都只在自己的世界里存在,不與其他線程共享數(shù)據。線程之間唯一的交互是send出去的消息,所以,當消息是不可變對象 或者 actor存儲的是消息副本時,就能保證兩個線程之間是獨立的。

妥善使用Actor模式,保證子類也符合這三個原則,那么程序在架構層面上,能很好地降低多線程應用程序設計的復雜度。

最后,我們發(fā)散一下思維。
這里PrintActor是最簡單的一種Actor,實踐中我們可以設計一個DatabaseActor,用來存儲數(shù)據;設計一個LogActor,用來記錄日志;甚至可以把send方法實現(xiàn)為在socket上的數(shù)據傳輸,將“發(fā)送信息”這一概念擴展到多進程甚至是分布式系統(tǒng)之中。

參考書籍:

  1. 《Python Cookbook》第三版
  2. 《代碼整潔之道》Robert C.Martin
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容