在我們編程的時候,經常會遇到一個概念——異步,諸如異步通信,異步線程,異步代碼,異步調用,異步編程等等,那麼
什麼是異步呢?
為什麼要異步?
異步的典型場景是什麼?
如何使用異步呢?
......
老碼農初識異步是從單片機的串行通信開始的。串行通信,是指通信雙方按位進行,遵守時序的一種通信方式。串行通信有兩種類型,一種是同步通信,另一種就是異步通信。
同步通信的特點是要求發送時鐘和接收時鐘保持嚴格的同步,異步通信的發送端和接收端可以由各自的時鐘來控制數據的發送和接收,這兩個時鐘源彼此獨立,互不同步。異步通信中的接收方並不知道數據什麼時候會到達,發送方發送的時間間隔可以不均勻,接收方是在數據的起始位和停止位的幫助下實現信息同步的。簡單的說,異步是扔出去一段數據,對方靠著內容前後所檢查到的特殊性發現了它,把這個內容存下來;而同步通信是對方在時刻等著發送方發號施令,發送方告訴對方要發送了,然後雙方一拍即合。
從通信效率來看,同步通信效率高,異步通信效率較低。但從實現方式來看,同步通信較複雜,異步通信相對簡單,計算機的接口大多是異步的。
進一步,對通信網絡而言,同步網一般是指網絡內所有電信設備的時鐘(或載波)提供同步控制信號,使它們的頻率工作在共同速率(或頻率)上的支撐網。同步網可分為準同步網和同步網兩類,由具有相同標稱頻率的不同基準時鐘互相比對的同步網稱為準同步網,由單一基準時鐘控制的稱為同步網。我國和大多數國家採用分級主從同步法,國家間採用準同步法。異步網絡不需要時間同步,可以在任何節點完成逐分組的轉發,這種分組的不可預測和不規則機制增加了網絡的阻塞率。然而,異步網絡具有同步網絡所不具備的低成本、低複雜度、高健壯性和高靈活性,通過合理設計交換的結構和協議,也可達到良好的交換性能。
關於通信網乃至TMN,太容易給人帶來回憶了。跳出湧現的往事,對程式設計師而言,異步的概念有了相當程度的延伸。
編程中的同步與異步往往是指兩個對象之間的調用關係:
對於異步編程而言,和軟體系統的架構設計有類似的地方,大體上,可以分為面向CPU的異步編程和面向IO的異步編程這兩種方式。
我們常見的多線程中就會經常遇到面向CPU的異步編程。線程是作業系統能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。同步線程是指兩個線程的運行是相關的,其中一個線程可能要阻塞等待另外一個線程的運行。異步線程是兩個線程毫不相關,自己運行自己的。
這裡也經常遇到另外的兩個概念——阻塞和非阻塞,在多線程編程中,主要是指線程是否需要等待。阻塞調用指調用結果返回之前,當前線程會被掛起。調用線程只有在得到結果之後才會返回。非阻塞調用是指在不能立刻得到結果之前,該調用不會阻塞當前線程。
在Android上編程的時候,UI主線程和子線程的交互幾乎是不可或缺的。在伺服器側,同樣如此,SpringBoot 中配置異步線程池的簡單示例如下:
//啟動異步@EnableAsync
//配置類
@Configuration
class ThreadsPoolConfig {
@Bean(&34;)
public Executor taskExecutor() {
//創建建線程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//初始的線程數量
executor.setCorePoolSize(INI_THREAD_NUM);
//最大的線程數量
executor.setMaxPoolSize(MAX_THREAD_NUM);
//隊列的最大容量
executor.setQueueCapacity(MAX_QUEUE_CAP);
//存活時間
executor.setKeepAliveSeconds(ALIVE_TIME);
//線程池的飽和策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//關閉線程池時是否等待任務完成
executor.setWaitForTasksToCompleteOnShutdown(true);
//等待終止的時間
executor.setAwaitTerminationSeconds(WAIT_TERMINATE_TIME);
return executor;
}
}
面向IO的操作,是異步編程的應用場所之一。在通過IO訪問數據的方式,同步編程需要主動讀寫數據,在讀寫數據的過程中還是可能會遇到阻塞;異步編程只需要I/O操作完成的通知,並不主動讀寫數據,而是由作業系統內核完成數據的讀寫。
在《Unix網絡編程》第二卷中,提到了5種IO模型:
前四種io模型為同步io模型,只有異步io模型與posix定義的io相匹配。異步IO在用戶進程觸發I/O操作以後就立即返回,繼續開始做自己的事情,而當I/O操作已經完成的時候會得到I/O完成的通知。異步IO的執行者是內核線程,內核線程將數據從內核態拷貝到用戶態,所以沒有阻塞。
Linux2.6以後引入了AIO,主流的IO機制可能是EPOLL,一種性能卓越且編程簡單的異步IO機制,在Nginx的配置中就可以看到它的身影。
網絡編程是一種特殊的IO操作,socket 編程中同樣存在著同步和異步調用。網絡編程的目的是網絡通信,而網絡通信的傳輸協議無外乎那八字的四原則:
在Python中,可以asyncio來實現網絡通信中的異步編程,asyncio庫包含異步IO、事件循環、協程、task等內容,事件循環是asyncio提供的核心運行機制。
從分布式系統的角度來看,消息隊列提供了異步通信的能力,藉助消息隊列,多個模塊間可以靈活的進行消息傳遞,這裡的異步通信就是指消息生產者可以將消息放入隊列中,而不等待結果返回,由消息隊列負責投遞消息給消費者。在系統設計中,我們可以通過消息隊列來進行模塊間的通信。
太多的概念了,其實都是為了試圖理解「異步」這一核心概念,還是舉個例子吧。
DBP開放平臺向開發者開放了技能內異步推送的機制,技能內推送意味著開發者能夠在用戶的會話周期內,異步調用推送接口向設備端推送相關內容或協議指令。典型的應用場景,包括銀行類耗時較長的操作處理,對用戶的異步通知等等。
目前DBP平臺提供了兩大類的異步推送,分別為文本和BOT協議。文本又分為純文本,使用該類型將在設備端底部展示一個通知,同時內容為文本內容;另一種是TTS,設備端將用語音播報相關的TTS;BOT協議提供了更豐富的設備端內容展示的情景。
使用服務之前需要先在DBP開放平臺申請該服務的權限:
編輯技能->配置服務,在服務權限配置下可以看到「技能內異步推送」,點「申請」,申請信息會加到審核列表裡,待運營人員審核通過後,服務的狀態將變成「已通過」,服務的權限即申請完成。若想下掉該權限,點擊「下線」即可下線服務。
依次點擊&34;,進入文本模板頁,該頁由DBP提供了部分通用的系統模板,開發者只需在調用相關接口時更改相關參數即可:
通配參數在模板裡用%%包含的字符串表示,同時該頁面提供了渲染模板的測試工具,填入相應的模板ID、參數的鍵值對,點擊生成後就能得到最終渲染完的文本內容:
點擊「BOT協議」導航,進入BOT協議模板列表頁,這裡列出了DBP當前支持使用的BOT協議模板:
如上圖,目前DBP提供了AudioPlayer.Play指令模板,使用該指令時,通過推送接口將會讓設備端調起AudioPlayer並播放指定的音頻。
點擊AudioPlayer.Play連結,進入詳情頁,詳情頁裡展示了該指令支持的欄位、欄位類型、可選、是否可自定義以及示例等信息,推送接口將會根據這些定義項進行數據校驗,開發者在使用時不要傳錯數據:
對於部分模板,DBP提供了可自定義的欄位,可以設置自定義欄位的鍵與類型,提交審核通過後,就可以使用了,目前支持的類型分別為STRING,INT, ARRAY, OBJECT, BOOLEAN,所填的欄位都是必須傳的,推送接口會校驗相應的欄位與類型:
BOT協議模板未審核通過前,可以先debug,debug時需要用戶綁定自己的設備SN,設備SN在設備的底部,每個技能最多只能綁定5個設備:
推送接口地址為:https://xiaodu.baidu.com/saiya/v1/notification/reprompt,method為POST,需要設置Content-Type為application/json:
$ curl --location --request POST &39; \
--header &39; \
--header &39; \
--data-raw &34;dialogRequestId&34;notificationType&34;plainText&34;templateId&34;1&34;debug&34;templateParams&34;userName&34;張三&34;botName&34;測試技能&39;
其中header裡要將{{apiAccessToken}}替換成實際的apiAccessToken,該值從BOT request裡獲取,在context.System.apiAccessToken裡。POST的body為json格式,其中:
例如,一個bot協議的推送請求如下:
curl --location --request POST &39; \
--header &39; \
--header &39; \
--data-raw &34;dialogRequestId&34;notificationType&34;botProtocol&34;templateId&34;AudioPlayer.Play&34;debug&34;templateParams&34;type&34;AudioPlayer.Play&34;playBehavior&34;REPLACE_ALL&34;audioItem&34;stream&34;url&34;token&34;7de36bb44852ae287028ba830565a6ef&34;playInfo&34;content&34;title&34;音頻測試&34;titleSubtext1&34;音頻&39;
許多工作和技術上的爭執源自概念的混淆,技術上的概念不同於文學作品——「每個人眼中都有一個自己的哈姆雷特」。討論問題的基礎好像應該是,澄清概念和明確問題的領域邊界。
異步是一個常見的概念,但在不同的場景中有著不同的含義,本文梳理一下相關內容,試圖可以澄清一些。