生活随笔
收集整理的這篇文章主要介紹了
iOS开发-10.多线程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
a
) NSThread
/ GCD
/ NSOperation底層都是pthreadb
) NSThread開啟線程方式
1 ) 動態實例化
NSThread
* thread
= [ [ NSThread alloc
] initWithTarget
: self selector
: @selector ( test
: ) object
: nil
] ;
thread
. threadPriority
= 1 ;
[ thread start
] ;
2 ) 靜態實例化
[ NSThread detachNewThreadSelector
: @selector ( test
: ) toTarget
: self withObject
: nil
] ;
3 ) 隱式實例化
3.1 )
[ self performSelectorOnMainThread
: @selector ( test
: ) withObject
: nil waitUntilDone
: YES
] ;
a
) GCD中有
2 個用來執行任務的函數
1 ) 用同步的方式執行任務
dispatch_sync ( dispatch_queue_t queue
, dispatch_block_t block
) ; 1.1 ) dispatch_sync
: 立馬在當前線程同步執行任務
, 不執行就不會往下走
2 ) 用異步的方式執行任務
dispatch_async ( dispatch_queue_t queue
, dispatch_block_t block
) ; 2.1 ) dispatch_async
: 不要求立馬在當前線程同步執行任務
, 等上一個任務
( 也可能是整個外部的大函數執行完畢
) 執行完了再執行b
) GCD源碼https
:
a
) GCD的隊列可以分為
2 大類型
1 ) 并發隊列(Concurrent Dispatch Queue)
1.1 ) 可以讓多個任務并發(同時)執行(自動開啟多個線程同時執行任務)
1.2 ) 并發功能只有在異步(dispatch_async)函數下才有效b
) 串行隊列(Serial Dispatch Queue)
1 ) 讓任務一個接著一個地執行(一個任務執行完畢后,再執行下一個任務)
2 ) 主隊列是一種特殊的串行隊列c
) 隊列的特點
: 排隊,FIFO,First In First Out 先進先出d
) Global Queue全局隊列的指針地址是相同的
, 而手動創建的隊列則地址是不相同的
a
) 同步和異步主要影響:能不能開啟新的線程
1 ) 同步:在當前線程中執行任務,不具備開啟新線程的能力
2 ) 異步:在新的線程中執行任務,具備開啟新線程的能力b
) 并發和串行主要影響:任務的執行方式
1 ) 并發:多個任務并發(同時)執行
2 ) 串行:一個任務執行完畢后,再執行下一個任務
a
) 使用sync函數往當前串行隊列中添加任務,會卡住當前的串行隊列(產生死鎖)b
) performSelector
: withObject
: afterDelay
: 1 ) 有afterDelay的方法都是跟Runloop有關
, 相當于添加了一個定時器到Runloop去執行 所以你要看當前執行任務的線程有沒有Runloop
2 ) 在主線程好時正常調用
, 但是會延遲
, 因為主線程默認就會開啟一個Runloop
, 延遲是因為主線程的任務執行順序是串行的
, 需要等上一個任務執行完畢
, 可以理解為包裝該線程調用的
{ } 范圍函數調用完畢
, 例如viewDidLoad方法調用完畢
3 ) 但是在子線程卻不好使
, 直接不調用
, 因為子線程沒有開啟Runloop來保活
a
) 思考:如何用gcd實現以下功能
1 ) 異步并發執行任務
1 、任務
2 2 ) 等任務
1 、任務
2 都執行完畢后,再回到主線程執行任務
3
a
) 資源共享
1 ) 1 塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源
2 ) 比如多個線程訪問同一個對象、同一個變量、同一個文件b
) 當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題c
) 多線程安全隱患示例
01 – 存錢取錢
d
) 多線程安全隱患示例
02 – 賣票
e
) 多線程安全隱患分析
f
) 多線程安全隱患的解決方案
1 ) 解決方案:使用線程同步技術
( 同步,就是協同步調,按預定的先后次序進行
) 2 ) 線程同步本質
: 是不能同時讓多條線程占用一個資源
2 ) 常見的線程同步技術是
: 加鎖
( 所有線程都用同一把鎖
) 3 ) 鎖的原理
: 首先判斷這把鎖有沒有被人加過
, 沒有就會加鎖
, 有被加過
, 就會等待
a
) OSSpinLock ( High
- level
- lock高級鎖
) 1 ) OSSpinLock叫做”自旋鎖”
, 等待鎖的線程會處于忙等(busy
- wait)狀態
( 相當于執行了一個
do while 循環
) 一直占用著CPU資源
2 ) iOS10
. 0 開始不推薦使用
, 目前已經不再安全,可能會出現優先級反轉問題
( 如果等待鎖的線程優先級較高,它會一直占用著CPU資源,優先級低的線程就無法釋放鎖
, 造成死鎖的現象
) 3 ) 需要導入頭文件#import
< libkern
/ OSAtomic
. h
>
b
) os_unfair_lock ( Low
- level
- lock低級鎖
) 1 ) os_unfair_lock用于取代不安全的OSSpinLock ,從iOS10開始才支持
2 ) 從底層調用看,等待os_unfair_lock鎖的線程會處于休眠狀態,并非忙等
3 ) 需要導入頭文件#import
< os
/ lock
. h
>
c
) pthread_mutex ( Low
- level
- lock低級鎖
) 1 ) mutex叫做”互斥鎖”,等待鎖的線程會處于休眠狀態
2 ) 需要導入頭文件#import
< pthread
. h
>
3 ) pthread_mutex – 遞歸鎖
3.1 ) 遞歸鎖
: 允許用同一條線程對一把鎖進行重復加鎖
4 ) pthread_mutex – 條件
4.1 ) 條件
( 等待條件
, 等不到條件就休眠
, 等待的時候解鎖
, 喚醒的時候加鎖
, 次數對等
, 一般用于線程之間的通信
)
d
) NSLock
1 ) NSLock是對mutex普通鎖的封裝
e
) NSRecursiveLock
1 ) NSRecursiveLock是對mutex遞歸鎖的封裝,API跟NSLock基本一致
f
) NSCondition
1 ) NSCondition是對mutex和cond的封裝
2 ) signal
: 信號發出的時機
, 決定了后續代碼的執行
, 因為在解鎖前和解鎖后
3 ) signal
: 信號
( 單個
) 和broadcast
: 廣播
( 多個
) 的區別
g
) NSConditionLock
1 ) NSConditionLock是對NSCondition的進一步封裝,可以設置具體的條件值
2 ) 初始化不設置條件值的時候,默認條件值就是
0 3 ) lock
: 直接加鎖,不等條件值,如果沒有加鎖,那就加鎖
4 ) lockWhenCondition
: 條件值為多少的時候才加鎖
5 ) unlockWithCondition
: 解鎖并且設置條件值
6 ) 在子線程執行的任務
, 可以達到依賴的效果
h
) dispatch_semaphore
1 ) semaphore叫做”信號量”
2 ) 信號量的初始值,可以用來控制線程并發訪問的最大數量
3 ) 信號量的初始值為
1 ,代表同時只允許
1 條線程訪問資源,保證線程同步
i
) dispatch_queue
1 ) 直接使用GCD的串行隊列,也是可以實現線程同步的
2 ) 不是說有多線程就需要加鎖
j
) @ synchronized
1 ) @ synchronized是對mutex遞歸鎖的封裝
2 ) 源碼查看:objc4中的objc
- sync
. mm文件
3 ) @ synchronized ( obj
) 內部會生成obj對應的遞歸鎖,然后進行加鎖、解鎖操作
4 ) 性能比較差
, 蘋果官方不推薦使用
, 所以代碼提示也沒有
9.iOS線程同步方案性能比較
10.自旋鎖、互斥鎖比較
a
) 什么情況使用自旋鎖比較劃算?
1 ) 預計線程等待鎖的時間很短
2 ) 加鎖的代碼(臨界區)經常被調用,但競爭情況很少發生
3 ) CPU資源不緊張
4 ) 多核處理器b
) 什么情況使用互斥鎖比較劃算?
1 ) 預計線程等待鎖的時間較長
2 ) 臨界區有IO操作
3 ) 臨界區代碼復雜或者循環量大
4 ) 臨界區競爭非常激烈
5 ) 單核處理器c
) 臨界區
: lock與unlock之間的代碼 d
) IO操作
: 文件操作
( 讀和寫的操作
)
a
) atomic用于保證屬性setter、getter的原子性操作,相當于在getter和setter內部加了線程同步的鎖b
) 可以參考源碼objc4的objc
- accessors
. mmc
) 它并不能保證使用屬性的過程是線程安全的
a
) 思考如何實現以下場景
1 ) 同一時間,只能有
1 個線程進行寫的操作
2 ) 同一時間,允許有多個線程進行讀的操作
3 ) 同一時間,不允許既有寫的操作,又有讀的操作b
) 上面的場景就是典型的“多讀單寫”,經常用于文件等數據的讀寫操作c
) iOS中的實現方案有
1 ) pthread_rwlock:讀寫鎖
2 ) dispatch_barrier_async:異步柵欄調用
2.1 ) 這個函數傳入的并發隊列必須是自己通過dispatch_queue_cretate創建的
2.2 ) 如果傳入的是一個串行或是一個全局的并發隊列,那這個函數便等同于dispatch_async函數的效果
a
) GNUstep是GNU計劃的項目之一,它將Cocoa的OC庫重新開源實現了一遍b
) 源碼地址:http
: c
) 雖然GNUstep不是蘋果官方源碼,但還是具有一定的參考價值
a
) lldb查看匯編指令級別代碼時一行一行的運行
1 ) stepi ( si
) 2 ) nexti ( ni
) : 也是一行一行
, 但是遇到函數調用會一筆帶過這個函數調用
3 ) c
: 直接跳到下一個斷點b
) 定義變量的時候同時給他賦值結構體可以這么干,但是不能直接給結構體賦值c
) 線程的任務一旦執行完畢,生命周期就結束了,無法再使用
總結
以上是生活随笔 為你收集整理的iOS开发-10.多线程 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。