本文只是對線程間通信傳遞數據方式總結比較,對于詳細的使用請自行查閱相關文檔,不在此做詳細介紹。
系列文章傳送門:
? iOS各種鎖總結
? iOS多線程總結
概述
對于線程間通信,一般多用于線程間傳遞數據及線程間同步控制,對于線程間同步控制已在iOS各種鎖總結中闡述,本文將著重介紹常用的線程間傳遞數據方式。
通信方式
主要包括傳統的可用于線程間通信的進程間通信方式,Mach
內核核心mach port
,NSObject
對象、GCD
及操作隊列等方式。
傳統方式
管道
由于iOS
沙盒機制的限制,命名管道文件路徑需要指定在應用內部,如臨時/tmp
目錄,這也限制了進程間通信方式,但不妨礙其線程間通信,不過其為“半雙工”通信,使用也存在許多限制,如原子性寫入數據長度受限于緩存大小、消息封裝及邊界、進程異常消息丟失等問題。
注意;管道阻塞模式打開會阻塞等待另一方讀或者寫打開,若在主線程打開需要保證管道已經在其他線程讀/寫打開,否則可能會打開失敗,且管道為半雙工通信,只能讀或者寫打開;
tem:提供一個即時創建臨時文件的地方,但不需要持久化,應用關閉之后就可能被刪除或系統在程序不運行時,會幫助我們清除掉(類似android中緩沖ACache類似sd卡、或應用專享目錄storage/emulated/0/Android/data/app_package_name/files與cache目錄)。
套接字
常用的本機通信為“域套接字”,多用于進程間通信,但也可以用于線程間通信,一般使用較少;對于指定socket
域套接字文件路徑也存在沙盒機制限制,并且需要處理消息邊界;
共享內存
本身線程間共享進程的內存空間,因此不需要專門使用進程間“共享內存”方式,可直接使用進程空間(如全局變量)來傳遞數據,并通過線程間同步方式來控制數據同步,如互斥鎖、信號量、同步鎖、讀寫鎖等;
共享存儲
如通過臨時文件、plist
文件、持久化存儲(如NSUserDefault
)等共享存儲的方式也可以傳遞數據,通過線程鎖來解決數據一致性問題。
Mach Port
mach
消息是Mach IPC的核心基礎,消息可以在在兩個port
端口(或稱為端點)之間傳遞,端口可以是單主機也可以是遠程機器,并解決了消息參數串行化、對齊、填充和字節順序問題,具體mach
消息可見unix
進程間通信。
對于底層的mach
api使用較少,并且需要深刻理解內核mach
對象框架,不過Core Foundation
和Foundation
為Mach Port
提供了高級API,在內核基礎上封裝的CFMachPort / NSMachPort作為runloop
源結合runloop
實現異步通信;其中蘋果官方給出的NSNotificaiton
通知轉發到指定線程處理,就是利用Mach Port
加入到轉發線程runloop
中來處理,具體可見iOS NSNotification
使用及原理實現。
mach相關的頭文件定義在<mach/message.h>相關的頭文件中,CFMachPort定義在<CFMachPort.h>中
其優勢在于結合runloop
來實現異步通信,并且更接近于內核態對象更為高效;
NSObject對象
主要是通過NSObject
對象提供的線程相關的api,如下:
//主線程
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//指定線程
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
對于指定線程,可將線程對象注冊到某個全局對象中,或者通過線程類方法來獲取指定線程;
其實現是結合
runloop
runtime
及條件鎖來實現,具體可參見iOS 查漏補缺 - PerformSelector、 iOS runloop由淺入深
典型的用途就是創建常駐線程,通過performSelector:onThread:withObject..
將攜帶參數的任務添加到常駐線程去處理,比如AFNetworking2.x
;
GCD
主要是向主隊列、全局隊列或者自定義隊列添加任務,利用block
會傳遞上下文來攜帶通信參數,并通過dispatch_sync
、dispatch_group
dispatch_barrier
等方式同步任務。
NSOperationQueue
主要是向操作隊列添加任務,通過添加依賴關系來控制同步,若需要傳遞數據可通過block
來截獲上下文數據,但注意對象的生命周期避免循環引用問題,具體可參見iOS多線程總結。
小結
對于線程間通信常見的是線程間同步控制,比如通過線程鎖、GCD
隊列、NSOperationQueue
操作隊列;若涉及到線程間同步傳遞數據,最有效的方式是通過共享的進程內存并結合線程鎖來控制數據同步;若涉及到線程間異步傳遞數據,可通過mach port
或者performSelector:onThread:withObject:waitUtilDone:
并結合runloop
來實現。
傳遞數據大小而言,對于大容量數據,一般會存儲到臨時文件并傳遞文件描述符;
具體的線程間通信方式需要根據實際的使用場景來選擇,如同步/異步、傳遞數據大小、單向/雙向通信等。