iOS面試-多線程

計(jì)算機(jī)多進(jìn)程發(fā)展的背景:

早期計(jì)算機(jī)工作是單道批處理程序處理,講寫好的作業(yè)卡依次放進(jìn)計(jì)算機(jī)的批處理程序執(zhí)行诅迷,效率非常低翁锡,后面出現(xiàn)了多道批處理程序,同一段時(shí)間允許多個(gè)程序同事快速執(zhí)行附帽,雖然效率很高埠戳,但是后面隨著硬件技術(shù)的跟進(jìn),除了分時(shí)系統(tǒng)處理作業(yè)這是已經(jīng)成為是進(jìn)程作業(yè)蕉扮;根據(jù)每個(gè)進(jìn)程的時(shí)間分片整胃,同一時(shí)間里快速切換執(zhí)行。后面又出現(xiàn)實(shí)時(shí)系統(tǒng)喳钟,更加高效屁使。。奔则。

多線程概念:

多線程是指在軟件或者硬件條件下可以實(shí)現(xiàn)多個(gè)線程并發(fā)執(zhí)行的技術(shù)蛮寂。具有多線程能力的計(jì)算機(jī)因有硬件的支持可以在同一時(shí)間執(zhí)行多個(gè)線程,進(jìn)而提高計(jì)算機(jī)的處理能力易茬。

多線程于進(jìn)程的關(guān)系:

進(jìn)程是程序執(zhí)行的基本單元酬蹋,而線程是程序執(zhí)行的基本單位及老,一個(gè)進(jìn)程可以擁有多個(gè)線程,并至少擁有一個(gè)線程范抓,每個(gè)線程獨(dú)立執(zhí)行骄恶,多個(gè)線程可以共享它所在進(jìn)程的代碼、數(shù)據(jù)匕垫、進(jìn)程空間和對(duì)應(yīng)的堆空間僧鲁;

多線程對(duì)程序開發(fā)帶來的便利:

  1. 單線程在遇到耗時(shí)操作的時(shí)候會(huì)阻塞UI線程,用戶界面的鼠標(biāo)點(diǎn)擊年缎、手勢(shì)滾動(dòng)都會(huì)立刻進(jìn)行執(zhí)行悔捶,造成了很不好的用戶體驗(yàn),多線程技術(shù)允許將耗時(shí)操作放進(jìn)單獨(dú)的線程里區(qū)執(zhí)行单芜,從而不會(huì)影響UI線程的交互蜕该,提高用戶體驗(yàn);
  2. 相對(duì)進(jìn)程來說洲鸠,多線程的創(chuàng)建堂淡、切換所需要cpu消耗、內(nèi)存占用量都會(huì)很少扒腕;
  3. 對(duì)于多核的操作系統(tǒng)绢淀,單一線程無法很好使用的多核資源,造成資源浪費(fèi)瘾腰。

多線程引進(jìn)的問題:

  1. 實(shí)現(xiàn)多線程的代碼會(huì)更加復(fù)雜皆的、難度、維護(hù)起來難蹋盆;
  2. 對(duì)于模型共享數(shù)據(jù)來時(shí)是多個(gè)線程共享的费薄,可能會(huì)出現(xiàn)線程死鎖的問題;
  3. 由于線程的創(chuàng)建栖雾、調(diào)度楞抡、切換都會(huì)消耗一定的cpu計(jì)算資源、內(nèi)存資源析藕、指令調(diào)度資源召廷,瘋狂的創(chuàng)建多線程會(huì)起到適得其反的后果;
  4. 數(shù)據(jù)不安全账胧,對(duì)于同一個(gè)共享區(qū)域的數(shù)據(jù)竞慢,可能會(huì)被篡改;

iOS開發(fā)過程中多線程實(shí)現(xiàn)的方案:

基于c語(yǔ)言的POSIX線程治泥,成為pthread 線程梗顺;是操作系統(tǒng)的多線程實(shí)現(xiàn)方案,由于oc中可以實(shí)現(xiàn)也可以使用c混編车摄,posix線程在iOS開發(fā)中也可以使用寺谤;posix線程的優(yōu)點(diǎn)是較為底層性能會(huì)高效仑鸥,但是由于c語(yǔ)言開發(fā),所以需要我們手動(dòng)的管理線程的內(nèi)容变屁,對(duì)于開發(fā)效率是一種不利眼俊;

nsthread 線程基于c語(yǔ)言封裝、面向?qū)ο蟮亩嗑€程實(shí)現(xiàn)方案粟关,api如下:

1.  init 方法實(shí)現(xiàn)多線程創(chuàng)建疮胖,該方法需要我們手動(dòng)執(zhí)行strart指令,將它放進(jìn)帶線程就緒隊(duì)列闷板,等待系統(tǒng)自己出發(fā)澎灸;
2. detachNewThreadSelector 方法創(chuàng)建并自動(dòng)放到線程就緒隊(duì)列等待系統(tǒng)自動(dòng)去出發(fā);

2.1 遮晚、 nsthread 優(yōu)點(diǎn):

1. nsthread提供適合oc開發(fā)人員使用多線程實(shí)現(xiàn)api,降低開發(fā)門檻性昭;
2. nsthread 提供了一套工具酷,我們可以快捷拿到多線程的信息和狀態(tài)县遣,例如: 是否是主線程糜颠、是否被取消,是否正在運(yùn)行等

2.2 萧求、nsthread 缺點(diǎn)

1. nsthread 的最大缺點(diǎn)是線程之間的通信比較麻煩其兴,也是設(shè)計(jì)的一種缺陷;

GCD

  1. 概念

gcd 我們稱為中央調(diào)度管理夸政,基于c接口元旬,是apple公司為了線程不能充分利用多核資源引入的一個(gè)多線程實(shí)現(xiàn)方案;它的工作流程是將一個(gè)事件分成若干小任務(wù)放入串行或者并發(fā)執(zhí)行隊(duì)列守问,遵循先進(jìn)先出的規(guī)則去加入線程執(zhí)行單元匀归,然后執(zhí)行其同步或者異步的執(zhí)行;
1.1 : 優(yōu)點(diǎn):

高效: 基于mach內(nèi)核線程調(diào)度酪碘;
線程安全:不用開發(fā)者關(guān)心線程的內(nèi)存問題朋譬;
使用簡(jiǎn)單盐茎,少代碼解決大問題兴垦;
  1. 核心機(jī)制-隊(duì)列

gcd 的核心內(nèi)容是隊(duì)列
gcd 的隊(duì)列分為: 串行、并發(fā)字柠、主隊(duì)列探越、全局隊(duì)列、組隊(duì)列

  1. 串行/并發(fā)/同步異步 組合方式對(duì)應(yīng)的結(jié)果
 1. 主線程的時(shí)間優(yōu)先
 2. 串行同步執(zhí)行: 執(zhí)行完一個(gè)任務(wù)窑业,再執(zhí)行下一個(gè)任務(wù)钦幔。不開啟新線程,都在主線程執(zhí)行常柄。
 3. 串行異步執(zhí)行: 開啟新線程鲤氢,但因?yàn)槿蝿?wù)是串行的搀擂,所以還是按順序執(zhí)行任務(wù)。
 4. 并發(fā)隊(duì)列異步執(zhí)行: 并發(fā)執(zhí)行卷玉,會(huì)開啟新的線程哨颂。
 5. 并發(fā)隊(duì)列同步執(zhí)行; 按順序執(zhí)行相种,不會(huì)開啟新的線程威恼。
 6. 主隊(duì)列同步事件: 
 7. 主隊(duì)列異步事件:  按次序執(zhí)行。
  1. gcd 的特別函數(shù):
    4.1 欄柵函數(shù) dispatch_barrier_(a)sync

當(dāng)我們需要異步事件同步去執(zhí)行的時(shí)候我們需要用函數(shù) dispatch_barrier_sync
dispatch_barrier_async 異步執(zhí)行

4.2 信號(hào)量

dispatch_semaphore 為了解決共享資源競(jìng)爭(zhēng)問題寝并,可以用信號(hào)量來決定資源是否可以被訪問箫措。
dispatch_semphore_create 創(chuàng)建一個(gè)信號(hào)量,參數(shù)是最大可以同事訪問的線程衬潦;
dispatch_semphore_waite 對(duì)信號(hào)量執(zhí)行減1操作斤蔓,如果結(jié)果大于0,就可以直接進(jìn)行資源訪問别渔;
dispatch_semphone_sign 對(duì)信號(hào)變量執(zhí)行加一操作附迷,訪問完成就要釋放對(duì)資源的占用權(quán)利,即信號(hào)量加1哎媚,不影響其他線程訪問喇伯。

4.3 延遲執(zhí)行函數(shù)

dispatch_after_t 函數(shù) 參數(shù): dispatch_time_t dispatch_queue_t block

4.4 組變量
當(dāng)我們需要多個(gè)請(qǐng)求后才能進(jìn)入下一步時(shí),我們需要下面的函數(shù)\

dispatch_group_t

1. 創(chuàng)建一個(gè)全局的隊(duì)列拨与,默認(rèn)優(yōu)先級(jí)
2. 創(chuàng)建一個(gè)group_t 
3. 創(chuàng)建group任務(wù)加入到全局隊(duì)列
4. 監(jiān)聽組隊(duì)列完成的notify dispatch_group_notify

// 加載圖片例子:
   NSLog(@"全部開始-----%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_async(group, queue, ^{
        sleep(4);
        NSLog(@"子線程1-----%@", [NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        sleep(3);
        NSLog(@"子線程2-----%@", [NSThread currentThread]);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部結(jié)束-----%@", [NSThread currentThread]);
    });

4.5 dispatch_group_enter/dispatch_group_leave 函數(shù)

4.4的任務(wù)是組任務(wù)力里只是簡(jiǎn)單的輸出一句話稻据,如果在里面加入一個(gè)異步請(qǐng)求就出出現(xiàn)問題:

有問題的代碼:

NSLog(@"全部開始-----%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.example.gcdDemo", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(4);
            NSLog(@"模擬請(qǐng)求1-----%@", [NSThread currentThread]);
        });
    });
    
    dispatch_group_async(group, queue, ^{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(3);
            NSLog(@"模擬請(qǐng)求2-----%@", [NSThread currentThread]);
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部結(jié)束-----%@", [NSThread currentThread]);
    });

它打印log: 全部開始-全部結(jié)束-任務(wù)2,任務(wù)1买喧, 我們發(fā)現(xiàn)這樣的話與我們的要求的不一樣捻悯,我們要求的是所有的任務(wù)都結(jié)束才會(huì)執(zhí)行notify 函數(shù)但是它提前執(zhí)行啦。

為了解決上面的問題需要用到 dispatch_group_enter/dispatch_group_leave
在組組任務(wù)內(nèi)部淤毛,每次進(jìn)行異步操作之前今缚,先執(zhí)行 dispatch_group_enter函數(shù),參數(shù)是group變量低淡; 當(dāng)內(nèi)部的任務(wù)完成時(shí)執(zhí)行dispatch_group_leave()
代碼如下:

NSLog(@"全部開始-----%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.example.gcdDemo", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(4);
            NSLog(@"模擬請(qǐng)求1-----%@", [NSThread currentThread]);
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(3);
            NSLog(@"模擬請(qǐng)求2-----%@", [NSThread currentThread]);
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部結(jié)束-----%@", [NSThread currentThread]);
    });

4.6 dispatch_apply 快蘇迭代函數(shù) 參數(shù): 迭代的次數(shù)姓言,所在的隊(duì)列,任務(wù)

4.7 dispatch_once : 只執(zhí)行一次任務(wù)蔗蹋,通常用在單例何荚,

5.gcd的常見問題
5.1 線程死鎖問題
原因:隊(duì)列引起- 場(chǎng)景:在主線程里執(zhí)行同步將任務(wù)添加到主隊(duì)列里
例子: 加入在viewdidload 方法中 我們同步執(zhí)行將block(執(zhí)行一句日志)添加祝隊(duì)列,在這易操作后面加上一個(gè)結(jié)束日志猪杭,運(yùn)行發(fā)現(xiàn)餐塘,block的日志、結(jié)束日志都沒有打印皂吮,通過現(xiàn)象說明是block來不及執(zhí)行戒傻、結(jié)束日志也無法執(zhí)行税手。原因是,首先程序首先將同步執(zhí)行函數(shù)加入到線程執(zhí)行單元里需纳,然后根據(jù)同步執(zhí)行函數(shù)要求將block加入到執(zhí)行主隊(duì)列里冈止,由于是同步函數(shù),只能等到block結(jié)束候齿,同步函數(shù)才能返回熙暴,而block在主隊(duì)列,它的機(jī)制是只有上一個(gè)出隊(duì)列的任務(wù)完成它才可以出隊(duì)列慌盯,這樣同步函數(shù)不能返回導(dǎo)致結(jié)束就無法按順序打印周霉,block無法執(zhí)行,導(dǎo)致同步函數(shù)無法返回亚皂,系統(tǒng)無法進(jìn)入下一步俱箱,就發(fā)錯(cuò)-死鎖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末灭必,一起剝皮案震驚了整個(gè)濱河市狞谱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌禁漓,老刑警劉巖跟衅,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異播歼,居然都是意外死亡伶跷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門秘狞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叭莫,“玉大人,你說我怎么就攤上這事烁试」统酰” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵减响,是天一觀的道長(zhǎng)靖诗。 經(jīng)常有香客問我,道長(zhǎng)辩蛋,這世上最難降的妖魔是什么呻畸? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任移盆,我火速辦了婚禮悼院,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咒循。我一直安慰自己据途,他們只是感情好绞愚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颖医,像睡著了一般位衩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上熔萧,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天糖驴,我揣著相機(jī)與錄音,去河邊找鬼佛致。 笑死贮缕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俺榆。 我是一名探鬼主播感昼,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼罐脊!你這毒婦竟也來了定嗓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤萍桌,失蹤者是張志新(化名)和其女友劉穎宵溅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體上炎,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡层玲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了反症。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辛块。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖铅碍,靈堂內(nèi)的尸體忽然破棺而出润绵,到底是詐尸還是另有隱情,我是刑警寧澤胞谈,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布尘盼,位于F島的核電站,受9級(jí)特大地震影響烦绳,放射性物質(zhì)發(fā)生泄漏卿捎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一径密、第九天 我趴在偏房一處隱蔽的房頂上張望午阵。 院中可真熱鬧,春花似錦、人聲如沸底桂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)籽懦。三九已至于个,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間暮顺,已是汗流浹背厅篓。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捶码,地道東北人贷笛。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像宙项,于是被迫代替她去往敵國(guó)和親乏苦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

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