多線程

主要涉及解決線程間同步及資源共享方面的解決方案。

iOS系統(tǒng)為我們提供的多線程技術方案有哪些肘交?
GCD(使用頻度最高)八堡,NSOperation(圖片異步下載),NSThread(常駐線程)









NSThread




文字簡述:(1)當創(chuàng)建好一個線程并開啟線程(CPU或許在調(diào)度其他的線程旷档,所以也許不會馬上調(diào)度新開啟的這個線程奴饮,因為CPU是來回切換執(zhí)行的),那么就會把這個線程對象放進圖中綠色的可調(diào)度線程池里面進入就緒狀態(tài)等待CPU調(diào)度迈倍,(2)假設過了一會CPU(CPU是切換執(zhí)行的伤靠,會分配給這個線程一個時間片)有時間了切換到這個線程上調(diào)度,那么這個線程就進入運行狀態(tài)啼染,如果這期間CPU在分配的時間片里沒有把這個線程執(zhí)行完畢又停下這個線程的執(zhí)行去調(diào)度其他的線程宴合,那么會記錄此時這個線程執(zhí)行的狀態(tài)再切換到其他線程,此刻這個線程狀態(tài)重新變?yōu)榫途w狀態(tài)(3)如果這個線程調(diào)用sleep方法/等待同步鎖迹鹅,那么這個線程就進入阻塞狀態(tài)卦洽,sleep到時/得到同步鎖,才會重新進入就緒狀態(tài)(4)線程的死亡有自然死亡和非自然死亡斜棚,自然死亡就是線程執(zhí)行完畢阀蒂,非自然死亡就是發(fā)生異常或者程序強制退出了




起了線程名字弟蚀,如若該線程出現(xiàn)異常蚤霞,可以快速定位到問題。




線程執(zhí)行的時間不由我們確定粗梭,每次的執(zhí)行都不確定争便,像剛剛上面這種情況線程是不安全的,不能保證數(shù)據(jù)和預期的結果是一致的断医。
下面是解決方案:用互斥鎖來防止因多線程搶奪資源造成的數(shù)據(jù)安全問題





注意??:如果[多線程]訪問同一個資源,那么必須使用同一把鎖才能鎖住奏纪,在開發(fā)中鉴嗤,盡量不要加鎖,能在服務端做盡量在服務端做序调,如果必須要加鎖醉锅,一定要記住,鎖的范圍不能太大发绢,哪里有[安全隱患]就加在哪里硬耍。
技巧:因為必須使用同一把鎖,開發(fā)中如果需要加鎖边酒,直接使用 self 即可经柴。
線程間通訊
子線程創(chuàng)建任務,在主線程更新UI

GCD

Test1

test1

答案1

Test1文字簡述:
viewDidLoad和Block都是在主線程調(diào)用墩朦,那么兩者都被加入主隊列(主隊列本身就是串行隊列)中坯认,(重點是隊列相互等待,而不是線程問題,不管是不是主隊列 是要是同一個隊列 都會發(fā)生死鎖)由于主隊列先進先出FIFO的特性牛哺,執(zhí)行完viewDidLoad才能執(zhí)行block陋气,可是block是同步在ViewDidLoad方法里,所以就造成了互相等待的死鎖問題引润。

Test2

Test2

答案二

test2文字簡述:
重點在于是加在了不同的隊列巩趁。block是加在一個自定義隊列,而不是與viewDidLoad同一個隊列淳附,所以先執(zhí)行viewDidLoad议慰,執(zhí)行到block的時候,因為是同步的方式 所以會先把serialQueue上的任務dosomething執(zhí)行完燃观,就會繼續(xù)往下執(zhí)行褒脯,所以是沒有問題的,不存在相互等待的問題

Test3

Test3

Test3答案簡述:輸出:1缆毁,2番川,3,4脊框,5颁督,這是對同步并發(fā)隊列的理解
global_queue為全局并發(fā)隊列,里面的任務為并發(fā)執(zhí)行。
按代碼順序輸出1浇雹,(同步方式提交任務到并發(fā)隊列還是串行隊列沉御,它都是在當前線程完成)同步方式提交一個任務到全局并發(fā)隊列中,那么打印2在主線程中執(zhí)行昭灵,同步方式提交一個任務到全局并發(fā)隊列中吠裆,那么打印3在主線程中執(zhí)行,然后打印4烂完,打印5

Test4

Test4

文字簡述:

  • performSelector:withObject:afterDelay:其實就是在內(nèi)部創(chuàng)建了一個NSTimer试疙,然后會添加到當前線程的Runloop中
  • 因為方法里有延遲執(zhí)行,所以內(nèi)部有NSTimer計時抠蚣,timer只有在runloop開啟的狀態(tài)下才能執(zhí)行祝旷,而這個block塊是異步方式,子線程的runloop默認情況下是關閉狀態(tài)嘶窄,所以不會調(diào)用printLog
    那么怎樣才可以打印1怀跛,2,3呢柄冲?把子線程的runloop打開不就好了嗎吻谋?是的 在log3上面,performSelector下面這個位置加上
    [[NSRunLoop currentRunLoop] run];
    那么疑問又來了 是不是在block塊里哪個位置都可以比如log1下面羊初?答案是不行的 下面截出run的一段官方文檔如下:
Discussion

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the `NSDefault<wbr>Run<wbr>Loop<wbr>Mode`by repeatedly invoking [runMode:beforeDate:](apple-reference-documentation://hcGlc34FMW). In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers. 

Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit. macOS can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting.

意思就是如果當前runloop沒有任何事件(source滨溉、timer什湘、observer)的話,那么它會馬上退出晦攒,所以應該寫在selector下面闽撤,這樣就可以保證開啟runloop的時候 有事件timer,那么runloop就不會退出了

Test5 多讀單寫

多讀單寫test5

多讀單寫方案:

dispatch_barrier_async(dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT), ^{
       //寫操作
    });
test4

為什么要同步讀取呢脯颜?因為獲得數(shù)據(jù)是想可以很快得到的哟旗,實現(xiàn)多讀就是objectforkey可以在多個線程調(diào)用,所以多個線程傳入并發(fā)隊列栋操,也就實現(xiàn)了多讀闸餐。

Test6: dispatch_group_async

Test6
 dispatch_queue_t concurrent_queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建一個隊列組
    dispatch_group_t group = dispatch_group_create();
    for (int i = 0; i < 3; i ++) {
        //異步組分派到并發(fā)隊列當中
        //在隊列組group中的concurrent_queue隊列中添加任務
        dispatch_group_async(group, concurrent_queue, ^{
           //下載
        });
    }
    //會執(zhí)行dispatch_group_notify這個方法,但是里面的block塊只會當隊列中的人物全部執(zhí)行完畢才會執(zhí)行
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       //當添加到組中的所有任務執(zhí)行完成之后會調(diào)用該block
    });

Test7

NSOperation

1.NSOperation任務執(zhí)行狀態(tài)有:

  • isReady. 當前任務是否就緒
  • isExcecuting 是否處于正在執(zhí)行中
  • isFinish 是否已完成
  • isCancel led 是否已取消

2.狀態(tài)控制:

  • 如果只重寫了main方法矾芙,底層控制變更任務執(zhí)行完成狀態(tài)舍沙,以及任務退出
  • 如果重寫了start方法,自行控制任務狀態(tài)

只重寫main源碼分析:首先看start方法:首先是創(chuàng)建一個自動釋放池剔宪,然后獲取線程的優(yōu)先級拂铡,然后進行一系列狀態(tài)異常判斷,如果沒有異常葱绒,會判斷是否處于正在執(zhí)行中感帅,如果是沒有執(zhí)行,則設置為正在執(zhí)行的狀態(tài)地淀,之后會在判斷當前任務是否被取消失球,如果沒有被取消,就會調(diào)用NSOperation 的 main函數(shù)帮毁,之后調(diào)用finish实苞,之后調(diào)用自動釋放池的release操作。


Test8 NSThead

Test8

test:NSThead啟動流程的內(nèi)部實現(xiàn):首先創(chuàng)建一個NSThead之后會調(diào)用start方法烈疚,然后來啟動線程硬梁,在start方法內(nèi)部會創(chuàng)建一個pThead線程,然后會指定pThead中的一個啟動函數(shù)胞得,在啟動函數(shù)中會調(diào)用pThead所定義的main函數(shù), 之后在main函數(shù)中會通過target performSelector來執(zhí)行目標函數(shù)selector,最后調(diào)用exit來結束這個線程
test: 如果要實現(xiàn)一個常駐線程的話屹电,可以在selector方法里維護一個runloop阶剑,實現(xiàn)事件的運行循環(huán),從而達到實現(xiàn)常駐線程的目的

Test8 鎖

iOS當中有哪些鎖危号?

  • @synchronized:一般在創(chuàng)建單例對象的時候使用牧愁,從而保證在多線程情況下創(chuàng)建對象是唯一的。
  • atomic:修飾屬性的關鍵字外莲;對被修飾對象進行原子操作(不負責使用猪半,就是只對賦值setter保證線程安全 但是對于可變數(shù)組的使用add delete不保證線程安全的)
  • OSSpinLock自旋鎖:循環(huán)等待訪問兔朦,不釋放當前資源(比如去房間有人會一直敲門),用于輕量級數(shù)據(jù)訪問磨确,簡單的int值 +1/-1操作
  • NSLock解決線程同步問題 來保證各個線程互斥進入自己的臨界區(qū)
  • NSRecursiveLock 遞歸鎖
  • dispatch_semaphore_t


    test8_1

    簡述:鎖已經(jīng)使用過了沽甥,所以需要等到鎖解開才可以再次訪問加鎖,線程被阻塞住了乏奥,這就造成了死鎖摆舟。


    解決8_1

    簡述:在這種情況下,我們就可以使用NSRecursiveLock邓了。它可以允許同一線程多次加鎖恨诱,而不會造成死鎖。遞歸鎖會跟蹤它被lock的次數(shù)骗炉。每次成功的lock都必須平衡調(diào)用unlock操作照宝。只有所有達到這種平衡,鎖最后才能被釋放句葵,以供其它線程使用厕鹃。
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市笼呆,隨后出現(xiàn)的幾起案子熊响,更是在濱河造成了極大的恐慌,老刑警劉巖诗赌,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汗茄,死亡現(xiàn)場離奇詭異,居然都是意外死亡铭若,警方通過查閱死者的電腦和手機洪碳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叼屠,“玉大人瞳腌,你說我怎么就攤上這事【涤辏” “怎么了嫂侍?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荚坞。 經(jīng)常有香客問我挑宠,道長,這世上最難降的妖魔是什么颓影? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任各淀,我火速辦了婚禮,結果婚禮上诡挂,老公的妹妹穿的比我還像新娘碎浇。我一直安慰自己临谱,他們只是感情好,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布奴璃。 她就那樣靜靜地躺著悉默,像睡著了一般。 火紅的嫁衣襯著肌膚如雪溺健。 梳的紋絲不亂的頭發(fā)上麦牺,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音鞭缭,去河邊找鬼剖膳。 笑死,一個胖子當著我的面吹牛岭辣,可吹牛的內(nèi)容都是我干的吱晒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沦童,長吁一口氣:“原來是場噩夢啊……” “哼仑濒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起偷遗,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤墩瞳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后氏豌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喉酌,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年泵喘,在試婚紗的時候發(fā)現(xiàn)自己被綠了泪电。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡纪铺,死狀恐怖相速,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鲜锚,我是刑警寧澤突诬,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站芜繁,受9級特大地震影響攒霹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜浆洗,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望集峦。 院中可真熱鬧伏社,春花似錦抠刺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至聪黎,卻和暖如春罕容,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稿饰。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工锦秒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人喉镰。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓旅择,卻偏偏與公主長得像,于是被迫代替她去往敵國和親侣姆。 傳聞我的和親對象是個殘疾皇子生真,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容