[Program Note] Asynchronous Programing與Callback Function

Asynchronous Programing

一開始要先清楚何謂同步與非同步,同步比較簡單,就像是我們去銀行辦事情,你填好單子交給行員,之後你必須在行員的櫃台一直等他完成作業,無論是開戶、領款、或者存款,這樣就是同步作業;你的程式送出request之後必須一直等待到response,才可以做其他事情。

而非同步作業時常與多執行緒作業畫上等號,實際上是不同的,多執行緒就像是有六十個人要在銀行開戶,所以銀行開了10個櫃台來處理這60個作業,但是每個櫃台實際上還是執行著上面說的同步作業。

而非同步作業是你今天要開戶,填好單子給行員之後,你可以去隔壁買飲料,點完飲料你可以去隔壁買衣服,店員幫你包裝衣服的時候你可以去7-11繳個電話費,然後飲料店叫到你的號碼了你回去拿飲料,服飾店員說你的衣服包好了你回去拿衣服,最後回到銀行剛好叫到你的號碼再去櫃檯拿你的存摺;對於程式而言,你的程式送出request之後,到收到response之間,程式可以去做其他事情。

當瞭解同步與非同步作業之後,會延伸一個問題,你要對誰送出的request跟誰會送給你response?對於這個程式角色他必須有一個特性,它必須一直都存在,當你送出一個request之後,它會接收、處理,然後回覆給你一個response,當你再次送出一個request的時候,它也會重複同樣的程序,就像一個等待你的request的迴圈一樣;這樣的設計模式(Design Pattern)有個名字叫反應堆模式(Reactor Pattern),或者叫事件迴圈(Event Loop)、Select迴圈(Select Loop)

下面這張圖就說明了反應堆迴圈的執行方式-等待事件、處理事件的無限迴圈;這個反應堆在程式執行期間會一直存在著,你給他一個request,就是一個event,它會處理完,給你response,然後繼續安靜的等待下一個request。
5.the%2Breacotr%2Bloop.png-Asynchronous Programing與Callback Function

Callback Function

接著說明一下callback function,在一般的程式中,你寫了一個function,然後你在程式碼中呼叫這個function,我們可以稱為「call」一個function;如果你寫了一個function,然後把這個function的指給了某一個套件、框架、或者其他的程式,這個function在合適的時間,會由這些套件、框架、或其他程式來呼叫,那這個function就會被稱為callback function,因為它被「callback」了。

將非同步作業與callback function放在一起說並不是callback function只出現在非同步作業,在一般的程式中也可以使用callback function,例如你有一個視窗中的button函式,當這個函式執行時會呼叫一個列印文件的函式,所以你觸發按下視窗的button這個事件之後,就會列印文件,這個列印文件的函式就可以被稱為callback function;簡單來說,callback就是你只定義當事件發生時要做什麼事情(callback function),但何時要做(callback)這件事情是由其他事件(函式、套件)來決定。

下圖是非同步與callback function最經典的說明圖(Twisted code是Python的一個非同步套件,可以替換為任何語言的非同步套件),虛線下方為我們撰寫的程式碼,在做了一些我們想做的事情之後,例如建立TCP連線、建立物件、印出歡迎訊息等,程式會進到非同步套件的反應堆中,當事件發生時,透過我們寫的callback function回到我們的程式中處理事件,處理完之後就繼續回到反應堆中等待事件。
6.the%2Breactor%2Bmaking%2Ba%2Bcallback.png-Asynchronous Programing與Callback Function

來看一段簡單的程式碼,這是用Python寫的MQTT subscribe:
def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))
    client.subscribe("test",)

def on_message(client, userdata, msg):
    print(msg.payload)

client = mqtt.Client("subclient")
client.on_connect = on_connect
client.on_message = on_message
client.connect("192.168.1.100", 1883, 60)
client.loop_forever()

第1-3行是宣告一個連接時要執行的callback function,執行的內容是印出連接的result code,並且訂閱一個叫test的topic。

第5-6行是宣告一個收到訊息時的callback function,它會印出收到的訊息。

第8行會建立一個mqtt的物件,這行也是這個程式執行時的進入點。

第9-10行是將我們剛才定義的callback function指給mqtt物件中負責執行這兩種callback的方法。

第11行定義mqtt物件連接的broker參數。

第12行的loop_forever()是我們說的反應堆,當程式執行到這邊便會將程式的執行權交給mqtt的反應堆,這時候反應堆會先用我們剛才定義的broker參數去建立連線,連線成功之後會觸發client.on_connect方法,client.on_connect就會callback我們定義的on_connect函式,印出連線result code與訂閱test topic,然後反應堆就等待mqtt的publish,一但有publish訊息進來的事件,就會觸發client.on_message方法,client.on_message就會callback我們定義的on_message函式將publish訊息輸出到畫面,之後反應堆又會繼續等待事件的發生。

留言