ios多線程

養(yǎng)成好習(xí)慣藕施,把學(xué)過的東西都留一手宋渔,如有錯(cuò)請(qǐng)指示

基本概念

  • 進(jìn)程

進(jìn)程是指系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序川陆,針對(duì)于iOS來說毁靶,就是開啟了一個(gè)app胧奔,這個(gè)app的運(yùn)行就是一個(gè)進(jìn)程

  • 線程

一個(gè)進(jìn)程要想執(zhí)行任務(wù),那么就必須得有線程预吆,一個(gè)進(jìn)程至少得有一個(gè)線程

  • 多線程的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

1.能適當(dāng)?shù)奶岣叱绦虻倪\(yùn)行效率
2.能適當(dāng)?shù)奶岣遚pu的利用率

缺點(diǎn)

1.開啟線程需要棧和寄存器等內(nèi)存消耗龙填,默認(rèn)的一條線程占用棧去512kb
2.線程過多會(huì)導(dǎo)致CPU在線程上的消耗比較大
3.線程過多,程序設(shè)計(jì)就復(fù)雜了

  • 并發(fā)與并行

    并發(fā)

    并發(fā):描述的是多個(gè)任務(wù)同時(shí)發(fā)生拐叉,都需要被處理岩遗,這是一種現(xiàn)象,側(cè)重點(diǎn)在發(fā)生

    并行

并行:指的是一種技術(shù)凤瘦,一個(gè)同時(shí)處理多個(gè)任務(wù)的技術(shù)宿礁,側(cè)重點(diǎn)在運(yùn)行

總結(jié)

我們說的多線程,其實(shí)就是采取了并行技術(shù)蔬芥,從而提高執(zhí)行效率梆靖,因?yàn)橛袀€(gè)多個(gè)線程控汉,所以計(jì)算機(jī)的多個(gè)cpu可以同時(shí)工作,處理不同線程內(nèi)的指令返吻,但是對(duì)于單核的cpu而言姑子,多線程其實(shí)cpu在多個(gè)線程不停的調(diào)度,并發(fā)是一種現(xiàn)象思喊,面對(duì)這一現(xiàn)象壁酬,我們首先創(chuàng)建多個(gè)線程,真正加快程序運(yùn)行速度的是并行技術(shù)恨课,也就是讓多個(gè)cpu同時(shí)工作舆乔,而多線程是為了讓cpu同時(shí)工作成為可能,而對(duì)于單核的cpu剂公,就是讓cpu能在各個(gè)線程調(diào)度稱為可能

NSOperation & NSOperationQueue

簡(jiǎn)介:把要執(zhí)行的任務(wù)封裝到一個(gè)操作對(duì)象(operation object)希俩,并將操作對(duì)象放入操作隊(duì)列(nsoperationqueue),然后系統(tǒng)就會(huì)自動(dòng)在執(zhí)行任務(wù)纲辽。至于同步還是異步颜武、串行還是并行請(qǐng)繼續(xù)往下看

  • NSOperation的兩個(gè)子類

    1.NSInvocationOperation

    2.NSBlockOperation

GCD

  • 同步任務(wù)與異步任務(wù)

任務(wù):即操作,在gcd當(dāng)中就是一個(gè)block拖吼,任務(wù)執(zhí)行的兩種方式:同步執(zhí)行 和 異步執(zhí)行

同步任務(wù)(sync)和異步任務(wù)(async)的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線程鳞上,直到block中的任務(wù)執(zhí)行完畢
同步任務(wù)(sync):它會(huì)阻塞當(dāng)前線程并等待block中的任務(wù)執(zhí)行完畢,然后當(dāng)前線程才會(huì)繼續(xù)往下運(yùn)行
異步任務(wù)(async):當(dāng)前線程會(huì)直接往下執(zhí)行吊档,不用等著當(dāng)前任務(wù)執(zhí)行完篙议,并不會(huì)阻塞當(dāng)前線程

同步:只能在當(dāng)前線程執(zhí)行任務(wù),不具備開啟新線程的能力
異步:可以在新的線程執(zhí)行任務(wù)怠硼,具備開啟新線程的能力

  • 隊(duì)列

    • 串行隊(duì)列:先進(jìn)先出隊(duì)列鬼贱,每次只執(zhí)行一個(gè)任務(wù),線程任務(wù)按先后順序逐個(gè)執(zhí)行(需要等待隊(duì)列里面前面的任務(wù)執(zhí)行完之后再執(zhí)行新的任務(wù))
  • 并發(fā)隊(duì)列:先進(jìn)先出隊(duì)列,不過可以形成多個(gè)任務(wù)并發(fā)香璃,也就是說这难,雖然也是FIFO,但是不同的是葡秒,它取出來一個(gè)任務(wù)就放到別的線程姻乓,然后再取出來一個(gè)放到別的線程,動(dòng)作很快同云,看起來所有的任務(wù)都是一起執(zhí)行的糖权,不過gcd會(huì)根據(jù)系統(tǒng)資源控制并行的數(shù)量,多個(gè)任務(wù)按添加順序一起開始執(zhí)行(不用等待前面的任務(wù)執(zhí)行完再執(zhí)行新的任務(wù)),但是添加間隔往往忽略不計(jì)炸站,所以看著像是一起執(zhí)行的
  • 主隊(duì)列:這個(gè)是一個(gè)特殊的串行隊(duì)列星澳,隊(duì)列中的每個(gè)任務(wù)一定執(zhí)行在主線程中,如果主線程上有任務(wù)在執(zhí)行,主隊(duì)列就不會(huì)調(diào)度任務(wù)
  • 總結(jié)

關(guān)于同步異步旱易、串行并行和線程的關(guān)系禁偎,下面通過一個(gè)表格來總結(jié)

屏幕快照 2020-02-18 下午12.27.47.png

可以看到腿堤,同步方法不一定在本線程如暖,異步方法方法也不一定新開線程(考慮主隊(duì)列)笆檀。

  • 開不開線程,取決于執(zhí)行任務(wù)的函數(shù),同步不開,異步才能開
  • 開幾條線程,取決于隊(duì)列,串行開一條,并發(fā)可以開多條(異步)

隊(duì)列是負(fù)責(zé)調(diào)度任務(wù)的,同步異步負(fù)責(zé)執(zhí)行任務(wù)
串行隊(duì)列和并發(fā)隊(duì)列的區(qū)別:會(huì)不會(huì)阻塞當(dāng)前隊(duì)列(阻塞的意思:隊(duì)列里的任務(wù)不用執(zhí)行完盒至,就可以拿出下一個(gè)任務(wù))
同步和異步的區(qū)別:會(huì)不會(huì)阻塞當(dāng)前線程(阻塞的意思:不用等待當(dāng)前的任務(wù)是否完成酗洒,就可以執(zhí)行下一個(gè)任務(wù))


image.png

串行隊(duì)列 并發(fā)隊(duì)列 demo

-(void)gcdDemo8{
    /*
     全局隊(duì)列 & 并發(fā)隊(duì)列
     1> 名稱,并發(fā)隊(duì)列取名字,適合于企業(yè)開發(fā)跟蹤錯(cuò)誤
     2> release,在MRC 并發(fā)隊(duì)列 需要使用的
        dispatch_release(q);//ARC 情況下不需要release !
     
     
     全局隊(duì)列 & 串行隊(duì)列
        全局隊(duì)列: 并發(fā),能夠調(diào)度多個(gè)線程,執(zhí)行效率高
            - 費(fèi)電
        串行隊(duì)列:一個(gè)一個(gè)執(zhí)行,執(zhí)行效率低
            - 省點(diǎn)
     
        判斷依據(jù):用戶上網(wǎng)方式
            - WIFI : 可以多開線程
            - 流量  : 盡量少開線程
     
     */
    //1.隊(duì)列
    dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i< 10; i++) {
        NSLog(@"  %d",i);
        dispatch_async(q, ^{
            NSLog(@"%@  %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"come here");
   
    
}



//MARK:  全局隊(duì)列 (本質(zhì)上并發(fā)隊(duì)列)
-(void)gcdDemo7{
    //全局隊(duì)列
    /* 參數(shù)
     1. 涉及到系統(tǒng)適配
     iOS 8   服務(wù)質(zhì)量
     QOS_CLASS_USER_INTERACTIVE    用戶交互(希望線程快速被執(zhí)行,不要用好使的操作)
     QOS_CLASS_USER_INITIATED      用戶需要的(同樣不要使用耗時(shí)操作)
     QOS_CLASS_DEFAULT             默認(rèn)的(給系統(tǒng)來重置隊(duì)列的)
     QOS_CLASS_UTILITY             使用工具(用來做耗時(shí)操作)
     QOS_CLASS_BACKGROUND          后臺(tái)
     QOS_CLASS_UNSPECIFIED         沒有指定優(yōu)先級(jí)
     iOS 7  調(diào)度的優(yōu)先級(jí)
     - DISPATCH_QUEUE_PRIORITY_HIGH 2               高優(yōu)先級(jí)
     - DISPATCH_QUEUE_PRIORITY_DEFAULT 0            默認(rèn)優(yōu)先級(jí)
     - DISPATCH_QUEUE_PRIORITY_LOW (-2)             低優(yōu)先級(jí)
     - DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后臺(tái)優(yōu)先級(jí)
     
     提示:尤其不要選擇BACKGROUND 優(yōu)先級(jí),服務(wù)質(zhì)量,線程執(zhí)行會(huì)慢到令人發(fā)指!!!
     
     
     2. 為未來使用的一個(gè)保留,現(xiàn)在始終給0.
     
     老項(xiàng)目中,一般還是沒有淘汰iOS 7  ,沒法使用服務(wù)質(zhì)量
     */
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    for (int i = 0; i< 10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@  %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"come here");
}


#pragma mark - <同步任務(wù)>

//MARK : 增強(qiáng)版同步任務(wù)
// 可以隊(duì)列調(diào)度多個(gè)任務(wù)前,指定一個(gè)同步任務(wù),讓所有的異步任務(wù),等待同步任務(wù)執(zhí)行完成,這就是依賴關(guān)系
// - 同步任務(wù),會(huì)造成一個(gè)死鎖!
-(void)gcdDemo6{
    //隊(duì)里
    dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    //任務(wù)
    void (^task)()=^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"%d   %@",i ,[NSThread currentThread]);
            if (i==5) {
                //1.用戶登錄
                dispatch_sync(q, ^{
                    for (int i = 0; i < 5; i++) {
                        NSLog(@"用戶登錄  %@",[NSThread currentThread]);
                    }
                });
            }
        }
        //2.支付
        dispatch_async(q, ^{
            NSLog(@"支付  %@",[NSThread currentThread]);
        });
        
        //3.下載
        dispatch_async(q, ^{
            NSLog(@"下載  %@",[NSThread currentThread]);
        });
   
    };
    dispatch_async(q, task);
//    NSLog(@"come here");
    
}

//MARK - 同步任務(wù)作用!
/**
 在開發(fā)中,通常會(huì)將耗時(shí)操作放后臺(tái)執(zhí)行,有的時(shí)候,有些任務(wù)彼此有"依賴"關(guān)系!
 
 例子: 登錄,支付,下載
 
 利用同步任務(wù),能夠做到任務(wù)依賴關(guān)系,前一個(gè)任務(wù)是同步任務(wù),哥么不執(zhí)行完,隊(duì)列就不會(huì)調(diào)度后面的任務(wù)
 */
-(void)gcdDemo5{
    dispatch_queue_t loginQueue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    //1.用戶登錄
    dispatch_sync(loginQueue, ^{
        NSLog(@"用戶登錄  %@",[NSThread currentThread]);
    });
    //2.支付
    dispatch_async(loginQueue, ^{
        NSLog(@"支付  %@",[NSThread currentThread]);
    });
    //3.下載
    dispatch_async(loginQueue, ^{
        NSLog(@"下載  %@",[NSThread currentThread]);
    });
    for (int i = 0; i< 10; i++) {
        NSLog(@"......%@",[NSThread currentThread]);
    }
    
    
}





//MARK : 并發(fā)隊(duì)列,同步執(zhí)行   和 串行隊(duì)列,同步執(zhí)行 效果一樣!
-(void)gcdDemo4{
    
    // 會(huì)開線程嗎?  順序執(zhí)行?  come here?
    //  不會(huì)          順序     最后
    
    //1.隊(duì)列 - 并發(fā) DISPATCH_QUEUE_CONCURRENT
    dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    //2.同步執(zhí)行任務(wù)
    for (int i = 0; i < 10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d",[NSThread currentThread],i);
        });
    }
    //哥么在主線程!
    NSLog(@"come here");
}


//MARK : 并發(fā)隊(duì)列,異步執(zhí)行
-(void)gcdDemo3{
    //1.隊(duì)列 - 并發(fā) DISPATCH_QUEUE_CONCURRENT
    dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    //2.異步執(zhí)行任務(wù)
    for (int i = 0; i < 10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d",[NSThread currentThread],i);
        });
    }
    //哥么在主線程!
    NSLog(@"come here");
}



//MARK: 串行隊(duì)列,異步任務(wù)
-(void)gcdDemo2{
    /**
     會(huì)開幾條線程?會(huì)順序執(zhí)行嗎?
     */
    //1.隊(duì)列 - 串行
    dispatch_queue_t q = dispatch_queue_create("test", NULL);
    
    //2.異步執(zhí)行任務(wù)
    for (int i = 0; i < 10; i++) {
        NSLog(@"%d------------",i);
        dispatch_async(q, ^{
            NSLog(@"%@ %d",[NSThread currentThread],i);
        });
    }
    //哥么在主線程!
    NSLog(@"come here");
}


//MARK:串行隊(duì)列,同步任務(wù)
/**
*   不會(huì)開啟線程,會(huì)順序執(zhí)行
*/
-(void)gcdDemo1{
    //1.隊(duì)列 - 串行
    
    /**
     1.隊(duì)列名稱:
     2.隊(duì)列的屬性: DISPATCH_QUEUE_SERIAL 標(biāo)示串行!
     */
    dispatch_queue_t q = dispatch_queue_create("test", NULL);
    
    //2.同步執(zhí)行任務(wù)
    for (int i = 0; i < 10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d",[NSThread currentThread],i);
        });
    }
}

主隊(duì)列


重點(diǎn):主隊(duì)列,以FIFO調(diào)度任務(wù),如果主線程上有任務(wù)在執(zhí)行,主隊(duì)列就不會(huì)調(diào)度任務(wù),如下代碼能體現(xiàn)
    - 主要是負(fù)責(zé)在主線程上執(zhí)行任務(wù)
- (void)gcdDemo1 {
    //主隊(duì)列是專門負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊(duì)列 --> 不會(huì)開線程
    
    
    //1.隊(duì)列 --> 已啟動(dòng)主線程,就可以獲取主隊(duì)列
    dispatch_queue_t q = dispatch_get_main_queue();
    
    //2.異步任務(wù)
    dispatch_async(q, ^{
        NSLog(@"111==%@",[NSThread currentThread]);
    });
    NSLog(@"come here");
    NSLog(@"come here1");
    NSLog(@"come here2");
    NSLog(@"come here3");
    
    
    dispatch_async(q, ^{
        NSLog(@"222==%@",[NSThread currentThread]);
    });
    [self test];
    
    /*
    這么理解哈枷遂,整個(gè)gcdDemo1e也相當(dāng)于任務(wù)在主隊(duì)列里執(zhí)行
     test 方法就一個(gè)耗時(shí)操作
     打印順序是
     come here
     come here1
     come here2
     come here3
     test的for循環(huán)打印
     3333 (3333是touch方法調(diào)用gcdDemo1e后的一個(gè)打印語句)
     111==
     222==
如果主線程上有任務(wù)在執(zhí)行,主隊(duì)列就不會(huì)調(diào)度任務(wù)
     所以在主隊(duì)列里異步執(zhí)行任務(wù)樱衷,就要等主隊(duì)列的其他任務(wù)都執(zhí)行完了,在執(zhí)行block的任務(wù)
     
     */
    
}


- (void)gcdDemo2 {
    //主隊(duì)列是專門負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊(duì)列 --> 不會(huì)開線程
    NSLog(@"這里!!");
    //1.隊(duì)列 --> 已啟動(dòng)主線程,就可以獲取主隊(duì)列
    dispatch_queue_t q = dispatch_get_main_queue();
    
    //2.同步任務(wù)  ==> 死鎖
    dispatch_sync(q, ^{
        NSLog(@"能來嗎? ");
    });
    NSLog(@"come here");
    
    /*
     為什么是死鎖呢
     跟gcdDemo1的解釋一樣
     
     整個(gè)gcdDemo2 包括gcdDemo2后面的都相當(dāng)于一個(gè)任務(wù)在主線程里執(zhí)行酒唉,
     所以必須要等這個(gè)任務(wù)都執(zhí)行完了再去執(zhí)行dispatch_sync的任務(wù)
     但是dispatch_sync又是阻塞當(dāng)前線程的矩桂,也就是說dispatch_sync下面的代碼要等到dispatch_sync里面的任務(wù)執(zhí)行完再去執(zhí)行,這不就相互等待痪伦,誰也等不到誰侄榴,所以就死鎖了
     */
    
}
  • GCD死鎖

1.死鎖1
    NSLog(@"111111");
    dispatch_sync(dispatch_get_main_queue(), ^{
       
        
        NSLog(@"2222222");
    });
    NSLog(@"3333333");

出現(xiàn)的打印現(xiàn)象:只打印了“111111”,并且主線程已卡死网沾,點(diǎn)擊啊什么的都沒有效果了癞蚕,這個(gè)就是傳說中死鎖,但是在xcode8之后死鎖直接報(bào)錯(cuò)辉哥,如下所示

Paste_Image.png

解釋:同步任務(wù)會(huì)阻塞當(dāng)前線程涣达,然后把block中的任務(wù),只有等到block中的任務(wù)完成后才會(huì)讓當(dāng)前線程繼續(xù)往下走证薇。這段代碼的步驟:打印完第一句后,dispatch_sync 立即阻塞當(dāng)前的主線程匆篓,這里關(guān)于死鎖的描述有些簡(jiǎn)書模糊浑度,“然后把 Block 中的任務(wù)放到 main_queue 中,可是 main_queue 中的任務(wù)會(huì)被取出來放到主線程中執(zhí)行鸦概,但主線程這個(gè)時(shí)候已經(jīng)被阻塞了箩张,所以 Block 中的任務(wù)就不能完成”,這里“main_queue 中的任務(wù)會(huì)被取出來放到主線程中執(zhí)行”描述不準(zhǔn)確窗市,應(yīng)該是main_queue中已經(jīng)有任務(wù)在執(zhí)行了先慷,而這個(gè)任務(wù)就包含了同步任務(wù),而同步任務(wù)中的block卻被放到了列隊(duì)底部咨察,由于同步任務(wù)需要阻塞當(dāng)前線程论熙,完成block才能繼續(xù)執(zhí)行,而隊(duì)列又無法彈出該block來執(zhí)行摄狱,因?yàn)檫@時(shí)候在隊(duì)列頂部的是包含了block的任務(wù)脓诡,形成了循環(huán)依賴

2.死鎖2
dispatch_queue_t cusQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"111111");
    dispatch_async(cusQueue, ^{
        
        NSLog(@"2222222");
        dispatch_sync(cusQueue, ^{
            NSLog(@"3333333");
        });
        NSLog(@"44444444");
    });
    NSLog(@"5555555");

出現(xiàn)的打印現(xiàn)象:

Paste_Image.png

解釋:
1.創(chuàng)建的cusQueue是serial,這個(gè)是串行隊(duì)列
2.打印出1111
3.dispatch_async是異步執(zhí)行无午,所以當(dāng)前線程不會(huì)阻塞,于是有了兩條線程這是并行的祝谚,那么22222和5555打印的先后是不確定的
4.注意宪迟,高潮來了。現(xiàn)在的情況和上一個(gè)例子一樣了交惯。dispatch_sync同步執(zhí)行次泽,于是它所在的線程會(huì)被阻塞,一直等到 sync 里的任務(wù)執(zhí)行完才會(huì)繼續(xù)往下席爽。于是 sync 就高興的把自己 Block 中的任務(wù)放到 queue 中意荤,可誰想 queue 是一個(gè)串行隊(duì)列,一次執(zhí)行一個(gè)任務(wù)拳昌,所以 sync 的 Block 必須等到前一個(gè)任務(wù)執(zhí)行完畢袭异,可萬萬沒想到的是 queue 正在執(zhí)行的任務(wù)就是被 sync 阻塞了的那個(gè)。于是又發(fā)生了死鎖炬藤。所以 sync 所在的線程被卡死了御铃。剩下的兩句代碼自然不會(huì)打印。

3.之前誤以為是死鎖

    dispatch_queue_t myQueue =dispatch_queue_create("myQueue", NULL);
    NSLog(@"111111   :%d",[NSThread isMainThread]);
    dispatch_sync(myQueue, ^{
        NSLog(@"3333333  :%d",[NSThread isMainThread]);
       // dispatch_sync(myQueue, ^{
        //    NSLog(@"44444  :%d",[NSThread //isMainThread]);
    //    });
    });
    
    NSLog(@"5555555  :%d",[NSThread isMainThread]);


之前認(rèn)為沈矿,同步任務(wù) + 串行隊(duì)列 就是死鎖上真,因?yàn)橹罢J(rèn)為同步任務(wù)會(huì)阻塞了當(dāng)前線程,而串行隊(duì)列的任務(wù)被取出來會(huì)在當(dāng)前線程執(zhí)行羹膳,所以就會(huì)被死鎖睡互,這個(gè)例子根第一個(gè)死鎖很相似,區(qū)別就是第一個(gè)例子是主隊(duì)列陵像,而這個(gè)例子是串行隊(duì)列就珠。那么現(xiàn)在開始解釋這個(gè)為什么不是死鎖
1.第一句代碼是創(chuàng)建一個(gè)串行隊(duì)列
2.第二句代碼的1111能打印出來這個(gè)毋庸置疑
3.重點(diǎn)來了,第三句代碼醒颖,同步任務(wù)會(huì)阻塞當(dāng)前線程妻怎,這個(gè)也是肯定的,把任務(wù)放入這個(gè)自己創(chuàng)建的串行隊(duì)列泞歉,并且這個(gè)隊(duì)列之前是沒有任務(wù)的逼侦,所以代碼會(huì)執(zhí)行完這個(gè)block里的代碼再返回繼續(xù)走之后的代碼,
4.所以順序是 111 333 555腰耙,并不會(huì)死鎖
5.而第一例子之所以會(huì)造成死鎖榛丢,那是因?yàn)榘讶蝿?wù)放入的是主隊(duì)了,而執(zhí)行sync那句代碼也是在主隊(duì)列中挺庞,執(zhí)行sync時(shí)線程已經(jīng)阻塞晰赞,再把block的任務(wù)取出來是必須要等sync返回才能執(zhí)行,因?yàn)閟ync是比block先入隊(duì)列,出隊(duì)列是先進(jìn)的先出宾肺,所以相互等待就是死鎖
6.如果這個(gè)例子溯饵,把注釋掉的代碼打開,那么也是死鎖锨用,因?yàn)榈诙€(gè)sync是串行隊(duì)列的第一個(gè)任務(wù)丰刊,而block是串行隊(duì)列的第二個(gè)任務(wù),于是又是相互等待造成死鎖

4.死鎖總結(jié)

死鎖的原因不是線程阻塞增拥,而是隊(duì)列阻塞
如果dispatch_sync()的目標(biāo)queue為當(dāng)前queue啄巧,會(huì)發(fā)生死鎖(并行queue并不會(huì))。使用dispatch_sync()會(huì)遇到跟我們?cè)趐thread中使用mutex鎖一樣的死鎖問題

  • GCD常用方法

    dispatch_after
      NSLog(@"111111");  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          NSLog(@"22222");
      });
      
      dispatch_time_t  delayTime = dispatch_time(DISPATCH_TIME_NOW, 3);
      dispatch_after(delayTime, dispatch_get_main_queue(), ^{
         
          NSLog(@"33333");
      });
    

    dispatch_after只是延時(shí)提交block掌栅,并不是延時(shí)后立即執(zhí)行的秩仆,dispatch_after不是很精確

    dispatch_apply
dispatch_queue_t cusQueue = dispatch_queue_create("cus", DISPATCH_QUEUE_CONCURRENT);
   
   //第一個(gè)參數(shù),3--block執(zhí)行的次數(shù)
   //第二個(gè)參數(shù)猾封,applyQueue--block任務(wù)提交到的隊(duì)列
   //第三個(gè)參數(shù)澄耍,block--需要重復(fù)執(zhí)行的任務(wù)
   dispatch_apply(3, cusQueue, ^(size_t index) {
       NSLog(@"current index %@",@(index));
       sleep(1);
   });
   NSLog(@"2222");

dispatch_apply:把一項(xiàng)任務(wù)放入隊(duì)列中多次執(zhí)行,串行隊(duì)列和并行隊(duì)列都行晌缘,它是同步執(zhí)行的函數(shù)齐莲,不會(huì)立刻返回,要等待block中的任務(wù)全部執(zhí)行完才返回

dispatch_apply的正確使用方法:為了不阻塞主線程磷箕,一般把dispatch_apply放入異步隊(duì)列中調(diào)用选酗,然后執(zhí)行完后通知主線程

dispatch_once

保證app在運(yùn)行期間,block中的代碼只執(zhí)行一次岳枷,個(gè)人用的最多的就是單例的使用中

dispatch group

有時(shí)候我們會(huì)有這種需求芒填,在剛進(jìn)去一個(gè)頁面需要發(fā)送兩個(gè)請(qǐng)求,并且某種特定操作必須在兩個(gè)請(qǐng)求都結(jié)束(成功或失斂辗薄)的時(shí)候才會(huì)執(zhí)行殿衰,最low的辦法第二個(gè)請(qǐng)求嵌套在第一個(gè)請(qǐng)求結(jié)果后在發(fā)送,在第二個(gè)請(qǐng)求結(jié)束后再執(zhí)行操作盛泡。還有就是只使用一個(gè)Serial Dispatch Queue播玖,把想要執(zhí)行的操作全部追加到這個(gè)Serial Dispatch Queue中并在最后追加某種特定操作,頗為復(fù)雜操作饭于。但是呢,我們這里介紹更高級(jí)的辦法使用dispatch group


dispatch_queue_t cusConQueue = dispatch_queue_create("cusConQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_t cusGroup = dispatch_group_create();
    
    dispatch_group_async(cusGroup, cusConQueue, ^{
        
        NSLog(@"執(zhí)行任務(wù)1");
    });
    
    dispatch_group_async(cusGroup, cusConQueue, ^{
        
        NSLog(@"執(zhí)行任務(wù)2");
    });
    
    dispatch_group_async(cusGroup, cusConQueue, ^{
        
        NSLog(@"執(zhí)行任務(wù)3");
    });
    
    dispatch_group_notify(cusGroup, cusConQueue, ^{
       
        NSLog(@"執(zhí)行所有任務(wù)后想要的操作");
    });
    NSLog(@"44444----");

上圖中的 1 2 3 4執(zhí)行的順序都不一定维蒙,因?yàn)樗麄兌际钱惒剑? 2 3任務(wù)都執(zhí)行完成后才會(huì)執(zhí)行 notif里的任務(wù)

上面的dispatch_group_notify還可以換成dispatch_group_wait,代碼如下

dispatch_queue_t cusConQueue = dispatch_queue_create("cusConQueue", DISPATCH_QUEUE_CONCURRENT);
  
  dispatch_group_t cusGroup = dispatch_group_create();
  
  dispatch_group_async(cusGroup, cusConQueue, ^{
      
      NSLog(@"執(zhí)行任務(wù)1");
  });
  
  dispatch_group_async(cusGroup, cusConQueue, ^{
      
      NSLog(@"執(zhí)行任務(wù)2");
  });
  
  dispatch_group_async(cusGroup, cusConQueue, ^{
      [NSThread sleepForTimeInterval:2];
      NSLog(@"執(zhí)行任務(wù)3");
  });

  dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1);
  
  long result=dispatch_group_wait(cusGroup, DISPATCH_TIME_FOREVER);
  
  if (result==0) {
      NSLog(@"任務(wù)執(zhí)行完成");
  }
  else{
      NSLog(@"任務(wù)執(zhí)行還在繼續(xù)");
  }
  NSLog(@"44444----");


dispatch_group_wait第二個(gè)參數(shù)指定為等待的時(shí)間(超時(shí))掰吕,屬于dispatch_time_t類型,在這里使用DISPATCH_TIME_FOREVER颅痊,意味著永久等待殖熟。如果dispatch group的處理尚未結(jié)束,就會(huì)一直等待斑响,它會(huì)阻塞線程菱属,所以不會(huì)放在主線程里執(zhí)行钳榨,所以如果group的任務(wù)沒有處理完,代碼是不會(huì)執(zhí)行dispatch_group_wait之后的代碼纽门,所以這里的打印 1 2 3 是無序薛耻,但是4一定是在最后

但是呢上面這種dispatch_group的排列執(zhí)行方式,是不會(huì)考慮block塊內(nèi)部的異步請(qǐng)求情況的赏陵,它只能保證把block內(nèi)的非異步直觀代碼執(zhí)行完饼齿,所以如果ABC三個(gè)任務(wù)中如果有執(zhí)行異步的請(qǐng)求,那么在dispatch_group_notify最終任務(wù)執(zhí)行中蝙搔,那個(gè)異步請(qǐng)求不一定毀掉結(jié)束缕溉,所以又給應(yīng)該介紹新的api

dispatch_group_enter/dispatch_group_leave

調(diào)用dispatch_group_enter這個(gè)方法標(biāo)志著一個(gè)代碼塊被加入了group,和dispatch_group_async功能類似吃型;
需要和dispatch_group_enter()证鸥、dispatch_group_leave()成對(duì)出現(xiàn);編譯器會(huì)強(qiáng)制識(shí)別當(dāng)出現(xiàn)dispatch_group_leave全部結(jié)束才執(zhí)行dispatch_group_notify


dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(5);
        NSLog(@"任務(wù)一完成");
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(8);
        NSLog(@"任務(wù)二完成");
        dispatch_group_leave(group);
    });
    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任務(wù)完成");
    });
dispatch_barrier_async

這個(gè)函數(shù)可以設(shè)置同步執(zhí)行的block勤晚,它會(huì)等到在它加入隊(duì)列之前的block執(zhí)行完畢后枉层,才開始執(zhí)行。在它之后加入隊(duì)列的block运翼,則等到這個(gè)block執(zhí)行完畢后才開始執(zhí)行

dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-1");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-2");
    });
    dispatch_barrier_async(concurrentQueue, ^(){
        NSLog(@"dispatch-barrier");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-3");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-4");
    });

如上都是GCD一些常用的方法返干,還有一些不常用也沒去做記錄了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市血淌,隨后出現(xiàn)的幾起案子矩欠,更是在濱河造成了極大的恐慌,老刑警劉巖悠夯,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件癌淮,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沦补,警方通過查閱死者的電腦和手機(jī)乳蓄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夕膀,“玉大人虚倒,你說我怎么就攤上這事〔瑁” “怎么了魂奥?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)易猫。 經(jīng)常有香客問我耻煤,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任哈蝇,我火速辦了婚禮棺妓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘炮赦。我一直安慰自己怜跑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布眼五。 她就那樣靜靜地躺著妆艘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪看幼。 梳的紋絲不亂的頭發(fā)上批旺,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音诵姜,去河邊找鬼汽煮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛棚唆,可吹牛的內(nèi)容都是我干的暇赤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宵凌,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鞋囊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瞎惫,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤溜腐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瓜喇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挺益,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年乘寒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了望众。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伞辛,死狀恐怖烂翰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚤氏,我是刑警寧澤刽酱,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站瞧捌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姐呐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一殿怜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧曙砂,春花似錦头谜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至笑陈,卻和暖如春际度,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涵妥。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工乖菱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蓬网。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓窒所,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親帆锋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吵取,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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