1. 程式人生 > >非同步呼叫和多執行緒的架構設計

非同步呼叫和多執行緒的架構設計

繼續分析架構設計,http call是ios程式常用的與伺服器通訊和同步資料的方式,框架層給我們提供了簡單的同步和非同步方法,今天就想分析分析,同步和非同步。

首先不考慮同步和非同步的內部實現,但就兩種方式的特性,兩種方法都能完成功能,可是具體到架構的時候我們用哪一種呢??

優先考慮非同步方法,為什麼,以為非同步方法的邏輯能更好的支援低耦合高內聚的設計模式思想,但是如果只是以優先使用非同步方法為前提進行程式設計,就會發現遇到很多非常痛苦的問題,好些時候我們在http請求時候要記錄context,好在回撥的時候接這context繼續執行邏輯,這時候我們就想這不明顯應該用同步方法麼,用非同步幹什麼?

So,其實問題出在這是用同步呼叫的思維設計非同步方法程式,當然彆扭了,為了context可能需要一個全域性變數,甚至把context傳如Api再讓Api回傳,這兩種方式顯然都不好,所以要用非同步的思維設計非同步程式。

最近又有一些想寫的了,上回其實已經想到用非同步的思維設計非同步程式,這回就說說程式設計到底改怎麼寫。

首先最常見的程式邏輯肯定是順序執行吧,為什麼呢,因為這符合自然規律,凡事當然有先後,即便是併發的也依然會有先後的次序,所以寫程式碼很容易就按照執行先後次序寫code。

舉個不那麼貼切的例子,執行一個任務,把汽車從熄火鎖閉狀態調整到勻速前進狀態,馬上就可以簡單抽像出幾個步驟

1. 開鎖

2. 擰鑰匙發動

3. 掛到前進擋

4. 踩油門

5. 調整方向

6. 看儀表盤保持速度

7. 結束

好,看起來很順暢按照這個順序寫code不就好了麼,可是這和同步非同步多執行緒有什麼關係呢?關係就在具體的邏輯實現和更加底層的功能實現上。

1. 程式入口肯定從開鎖起,開鎖比較快,基本上可以做到馬上返回結果,true or false一個分支接著寫邏輯就好,同步操作沒有什麼異議,不過放在子執行緒還是主執行緒呢?

主執行緒一般都是用於互動的,那麼開鎖肯定是使用者發起的操作,開鎖的邏輯簡單那麼可以直接寫在主執行緒馬上返回即可。

2. 擰鑰匙發動,這又是一個貌似簡單的過程,但是這次就要複雜點了,從點火到發動機開始工作肯定是一個複雜的邏輯過程,而且車況不好可能還得多試幾次,不過因為時間短,繼續寫同步方法在主執行緒,因為發動不起來後面就不能執行,這樣也沒有問題

3. 掛檔,這一步相當於發指令給變速箱,變速箱做出響應,進入前進模式,但是檔位的變化需要一定的時間去完成所以考慮做成非同步操作

4. 踩油門,又是一個指令,依然需要時間,非同步

5. 調整方向,立竿見影同步主執行緒

6. 看儀表盤,觀察者模式,達到一定速度開始收油門響應,保持速度

7. 保持狀態量,讓步驟6的邏輯保持執行

ok,寫完了,這就是常見的寫法,功能實現邏輯完整沒有什麼問題,run一起,估計也差不到哪去,但是寫的時候會發現好多彆扭的地方,而且最後的效果也未必夠好,使用者不滿意,咋辦,作為程式還要考慮優化的問題,因為這些步驟可能以後還會加新東西。

a. 3,4是兩個非同步操作,順序寫好的程式碼到這裡就斷掉了,因為不能順序的執行,為什麼呢?因為雖然寫成了非同步操作但是還是需要等待它執行完成才能走5,6步驟的,這樣就變成了挖坑了,明明需要同步卻不得不因為非同步操作破壞掉通順的程式碼邏輯, 到這裡就得好好想想了,這好彆扭啊為什麼會這樣呢,現實成活中很簡單的事情為什麼必須用非同步來做呢,因為程式就不是現實,程式是基於計算機的原理來實現的,一個cpu,就一個cpu,運算都靠一個cpu所以程式是通過計算機原理去模擬現實世界的邏輯關係,關鍵就在模擬,它是讓執行看起來和客觀現實一樣,底下走的必然是圍繞cpu進行的甚至完全不同的過程。

使用者互動優先順序最高,不然就是宕機了,所以甭管你需要做什麼樣的操作,讓使用者感覺好才行,所以一切互動都優先在主執行緒,遇到耗時需要等待的時候怎麼辦,UI得接著重新整理,邏輯上完全同步的行為就busyindicator,邏輯上可以讓使用者先幹別的就得多執行緒伺候著,這才像個樣子,但是程式寫成這樣你就只能忍著了,沒轍,為什麼呢,因為這還是順序程式設計,根本不是面向物件,你沒能把介面和具體的邏輯實現分開,所以想區域性重構都沒戲,如果剛才的步驟設計程式結構的時候不是這樣,而是先抽象出核心的操作來

a. 入口 解鎖

b. 讓發動機工作起來

c. 給變速箱發指令讓改變發動機狀態

d. 改變車的方向

e. 保持發動機和車的方向狀態

ok,現在步驟看起來就完全不是那麼回事了,但是程式設計這才是開始,你要把現實的步驟抽象成符合程式邏輯東西,面向物件麼,發動機肯定是封裝好的一個類可以讓你做很多操作,變速箱是在發動機更高層的一個封裝,改變方向的操作很可能是封裝在懸架或者其他某些汽車部件類裡面的。所以你可以得到如下的結構

首先車是我們要給最上層呼叫的最高層封裝,最初的1-7只是車這個類的公共方法,這是給互動層呼叫的,所以寫7個函式宣告就ok了,而這裡的1-5要寫成介面,並且全部可以寫成非同步的形式,下面來點物件的事情

發動機提供prepare, ready, run, rate, 以及很多其他的狀態和switchto 方法, 變速箱提供gears 狀態,和changeTo方法,汽車的一系列部件提供redirect方法,

把具體的非同步,多執行緒的同步等等操作都封裝在這5個接口裡面,因為這五個介面是解耦合,他們彼此並沒有直接的關聯關係,所以用非同步和多執行緒的時候就不會有任何彆扭的感覺了。

等到都寫完了,再回頭到那7個公共方法上去

可以在車這個類裡面維護幾個狀態,檔位,轉速,方向,然後從2開始

2. 呼叫b,在回撥裡修改車的各種狀態,最終得出車已經readyTo換擋的結論再給回撥到使用者層,讓使用者可以執行3,然後依次類推,這樣就很順暢了1-7完全是同步過程又不需要考慮具體的邏輯實現,非同步和多執行緒都是封裝在更底層的類裡面的和最上層的呼叫沒有直接邏輯關係,這才是面向物件啊。