iOS 多線程-GCD

本文內(nèi)容
任務(wù)、隊(duì)列的概念匙赞、創(chuàng)建方式
任務(wù) + 隊(duì)列的6種組合的執(zhí)行方式
線程間如何通信
dispatch_once屡穗、dispatch_after、dispatch_apply(快速迭代)悼凑、dispatch_barrier(柵欄函數(shù))偿枕、dispatch_group(隊(duì)列組)、dispatch_semaphore(信號(hào)量)如何實(shí)現(xiàn)線程安全與線程同步
iOS多線程demo地址

上文說(shuō)到iOS 多線程- pThread和NSThread
這篇文章來(lái)講講GCD

GCD ??????????的優(yōu)點(diǎn)

  1. 可用于多核的并行運(yùn)算
  2. 會(huì)自動(dòng)利用更多的CPU內(nèi)核
  3. 自動(dòng)管理線程的生命周期(創(chuàng)建線程户辫、調(diào)度任務(wù)渐夸、銷毀線程)
  4. 只用關(guān)注執(zhí)行什么任務(wù),不用編寫任何線程管理代碼

1.任務(wù)

任務(wù):執(zhí)行的操作渔欢,放在block中的代碼

執(zhí)行任務(wù)的方式有兩種墓塌,主要區(qū)別是:是否等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束,是否具備開啟新線程的能力奥额。

  1. 同步執(zhí)行(sync):同步添加當(dāng)前任務(wù)到指定的隊(duì)列中苫幢,在隊(duì)列中的任務(wù)全部結(jié)束之前,會(huì)一直等待垫挨,直到隊(duì)列中的任務(wù)全部完成后韩肝,才開始下一個(gè)任務(wù),只能在當(dāng)前線程中執(zhí)行任務(wù)九榔,不具備開啟線程的能力哀峻。

  2. 異步執(zhí)行 (async):異步添加當(dāng)前任務(wù)到指定隊(duì)列中涡相,不會(huì)等待隊(duì)列的任務(wù)執(zhí)行結(jié)束,直接開始執(zhí)行下一個(gè)任務(wù)剩蟀,可以在新的線程中執(zhí)行任務(wù)催蝗,具備開啟線程的能力。

*注意:異步執(zhí)行 (async)雖然具有開啟線程的能力育特,但是不一定會(huì)開啟新的線程丙号,這跟任務(wù)所指定的隊(duì)列有關(guān)

2.隊(duì)列

隊(duì)列:存放任務(wù)的隊(duì)列,隊(duì)列是一種特殊的線性表缰冤,采用FIFO(先進(jìn)先出)的原則犬缨,即新任務(wù)總是被插入到隊(duì)列末尾,而讀取任務(wù)總是從隊(duì)列的頭部開始讀取锋谐,每讀取一個(gè)任務(wù)遍尺,隊(duì)列中則釋放一個(gè)任務(wù)。

隊(duì)列有兩種方式涮拗,都滿足FIFO原則乾戏,主要區(qū)別是:執(zhí)行順序不同,開啟線程數(shù)不同

  1. 串行隊(duì)列(Serial Dispatch Queue ):只開啟一個(gè)線程三热,一個(gè)任務(wù)執(zhí)行完畢后鼓择,再執(zhí)行下一個(gè)任務(wù)
  2. 并行隊(duì)列(Concurrent Dispatch Queue)?????????????? ??????????????????????????????????????????????????????????????:可以開啟多個(gè)線程,并且同時(shí)執(zhí)行多個(gè)任務(wù)

3.使用步驟

  1. 創(chuàng)建一個(gè)隊(duì)列
  2. 將任務(wù)添加到隊(duì)列中就漾,系統(tǒng)根據(jù)任務(wù)執(zhí)行方式(同步呐能、異步)進(jìn)行執(zhí)行

3.1 隊(duì)列的創(chuàng)建、獲取

使用dispatch_queue_create創(chuàng)建隊(duì)列
第一個(gè)參數(shù):隊(duì)列的唯一標(biāo)識(shí)符抑堡,用于DEBUG摆出,可以為空,推薦使用應(yīng)用程序ID這種逆序全局域名首妖。
第二個(gè)參數(shù):隊(duì)列類型偎漫,串行隊(duì)列DISPATCH_QUEUE_SERIAL,并行隊(duì)列DISPATCH_QUEUE_CONCURRENT

    //創(chuàng)建串行隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.queque", DISPATCH_QUEUE_SERIAL);
    //創(chuàng)建并行隊(duì)列
    dispatch_queue_t queue2 = dispatch_queue_create("com.xiuxiu.queque", DISPATCH_QUEUE_CONCURRENT);
     

主隊(duì)列Main Dispatch Queue: GCD提供一種特殊串行隊(duì)列:

  1. 所有放在主隊(duì)列中的任務(wù)有缆,都會(huì)放到主線程中執(zhí)行
  2. 可使用dispatch_get_main_queue()獲取主隊(duì)列

主隊(duì)列獲取方法

    dispatch_queue_t queue = dispatch_get_main_queue()

全局并發(fā)隊(duì)列Global Dispatch Queue : GCD默認(rèn)提供的全局并發(fā)隊(duì)列

并發(fā)隊(duì)列獲取方法:

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_get_global_queue獲取全局隊(duì)列
第一個(gè)參數(shù):隊(duì)列的優(yōu)先級(jí)

#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

第二個(gè)參數(shù):沒(méi)有使用象踊,用0 即可

3.2 任務(wù)的執(zhí)行方式


    dispatch_async(queue, ^{
         //異步執(zhí)行任務(wù)代碼
    });
    
    dispatch_sync(queue, ^{
            //同步執(zhí)行任務(wù)代碼
    });

第一個(gè)參數(shù)是隊(duì)列,那么隊(duì)列 + 任務(wù) 執(zhí)行方式就有6種組合(加上主隊(duì)列)

同步執(zhí)行 + 串行隊(duì)列
異步執(zhí)行 + 串行隊(duì)列
同步執(zhí)行 + 并行隊(duì)列
異步執(zhí)行 + 并行隊(duì)列
同步執(zhí)行 + 主隊(duì)列
異步執(zhí)行 + 主隊(duì)列

3.2.1.同步執(zhí)行 + 串行隊(duì)列

/*
 同步執(zhí)行 + 串行隊(duì)列
 不會(huì)開啟新線程棚壁,在當(dāng)前線程中執(zhí)行任務(wù)杯矩,一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)
 */
- (void)syncSerial{
    NSLog(@" syncSerial  start");
    dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.syncSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一袖外,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_sync(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二史隆,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    NSLog(@" syncSerial  end");
}

前面的編號(hào)代表進(jìn)程的編號(hào),一個(gè)APP的就是一個(gè)進(jìn)程曼验,進(jìn)程編號(hào)總是一致的泌射;
后面的編號(hào)代表線程的編號(hào)头镊, 21898代表線程的編號(hào)

輸出結(jié)果:

  1. 在當(dāng)前線程中執(zhí)行任務(wù),沒(méi)有開啟新線程(同步執(zhí)行不具備開啟線程能力)魄幕,根據(jù)線程編號(hào)看出
  2. 所有任務(wù)都在syncSerial startsyncSerial end 之間執(zhí)行(同步執(zhí)行需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束)
  3. 任務(wù)按順序執(zhí)行(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,一個(gè)任務(wù)執(zhí)行完畢后颖杏,再執(zhí)行下一個(gè)任務(wù))
2018-12-27 10:19:08.371664+0800 Thread[908:21898]  syncSerial  start
2018-12-27 10:19:08.371980+0800 Thread[908:21898]  任務(wù)一纯陨,i = 0
2018-12-27 10:19:09.372354+0800 Thread[908:21898]  任務(wù)一,i = 1
2018-12-27 10:19:10.372865+0800 Thread[908:21898]  任務(wù)一留储,i = 2
2018-12-27 10:19:11.373588+0800 Thread[908:21898]  任務(wù)二翼抠,i = 0
2018-12-27 10:19:12.374936+0800 Thread[908:21898]  任務(wù)二,i = 1
2018-12-27 10:19:13.375258+0800 Thread[908:21898]  任務(wù)二获讳,i = 2
2018-12-27 10:19:14.376006+0800 Thread[908:21898]  syncSerial  end


3.2.2.異步執(zhí)行 + 串行隊(duì)列

/*
 異步執(zhí)行 + 串行隊(duì)列
 會(huì)開啟新線程阴颖,但是因?yàn)殛?duì)列是串行的,一個(gè)任務(wù)執(zhí)行完畢后丐膝,再執(zhí)行下一個(gè)任務(wù)
 */
- (void)asyncSerial{
    NSLog(@" asyncSerial  start");
    dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.asyncSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一量愧,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    NSLog(@" asyncSerial  end");
}

輸出結(jié)果:

  1. 開啟一條線程(異步執(zhí)行具備開啟線程的能力帅矗,因?yàn)槭谴嘘?duì)列偎肃,只開啟了一條線程)
  2. 所有任務(wù)都在asyncSerial startasyncSerial end 之后執(zhí)行(異步執(zhí)行不會(huì)等待,繼續(xù)執(zhí)行任務(wù))
  3. 任務(wù)按順序執(zhí)行(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行浑此,任務(wù)一個(gè)接著一個(gè)執(zhí)行)
2018-12-27 10:43:17.947620+0800 Thread[1105:32316]  asyncSerial  start
2018-12-27 10:43:17.947916+0800 Thread[1105:32316]  asyncSerial  end
2018-12-27 10:43:17.948034+0800 Thread[1105:32360]  任務(wù)一累颂,i = 0
2018-12-27 10:43:18.953286+0800 Thread[1105:32360]  任務(wù)一,i = 1
2018-12-27 10:43:19.956284+0800 Thread[1105:32360]  任務(wù)一凛俱,i = 2
2018-12-27 10:43:20.961804+0800 Thread[1105:32360]  任務(wù)二紊馏,i = 0
2018-12-27 10:43:21.965620+0800 Thread[1105:32360]  任務(wù)二,i = 1
2018-12-27 10:43:22.967181+0800 Thread[1105:32360]  任務(wù)二蒲犬,i = 2

3.2.3. 同步執(zhí)行 + 并行隊(duì)列

/*
 同步執(zhí)行 + 并行隊(duì)列
 不會(huì)開啟新線程朱监,在當(dāng)前線程中執(zhí)行任務(wù),一個(gè)任務(wù)執(zhí)行完畢后暖哨,再執(zhí)行下一個(gè)任務(wù)
 */

- (void)syncConcurrent{
    NSLog(@" syncConcurrent  start");
    dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.syncConcurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一赌朋,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_sync(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    NSLog(@" syncConcurrent  end");
}

輸出結(jié)果:

  1. 在當(dāng)前線程中執(zhí)行任務(wù)篇裁,沒(méi)有開啟新線程(同步執(zhí)行不具備開啟線程能力)沛慢,根據(jù)線程編號(hào)看出,上面講過(guò)
  2. 所有任務(wù)都在syncConcurrent startsyncConcurrent end 之間執(zhí)行(同步執(zhí)行需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束)
  3. 任務(wù)按順序執(zhí)行达布,雖然并行隊(duì)列可以開啟多個(gè)線程团甲,并且同時(shí)執(zhí)行多個(gè)任務(wù),但是因?yàn)橥綀?zhí)行不具備開啟線程的能力黍聂,只有當(dāng)前這一個(gè)線程躺苦,而且同步執(zhí)行需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束身腻,再執(zhí)行下一個(gè)任務(wù),所以任務(wù)只能一個(gè)接一個(gè)按照順序執(zhí)行匹厘。
2018-12-27 10:54:22.992819+0800 Thread[1210:37289]  syncConcurrent  start
2018-12-27 10:54:22.993023+0800 Thread[1210:37289]  任務(wù)一嘀趟,i = 0
2018-12-27 10:54:23.994423+0800 Thread[1210:37289]  任務(wù)一,i = 1
2018-12-27 10:54:24.995012+0800 Thread[1210:37289]  任務(wù)一愈诚,i = 2
2018-12-27 10:54:25.996057+0800 Thread[1210:37289]  任務(wù)二她按,i = 0
2018-12-27 10:54:26.997429+0800 Thread[1210:37289]  任務(wù)二,i = 1
2018-12-27 10:54:27.998885+0800 Thread[1210:37289]  任務(wù)二炕柔,i = 2
2018-12-27 10:54:29.000327+0800 Thread[1210:37289]  syncConcurrent  end

3.2.4.異步執(zhí)行 + 并發(fā)隊(duì)列

/*
 異步執(zhí)行 + 并行隊(duì)列
 開啟多個(gè)線程酌泰,任務(wù)交替執(zhí)行
 */

- (void)asyncConcurrent{
    NSLog(@" asyncConcurrent  start");
    dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二匕累,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    NSLog(@" asyncConcurrent  end");
}

輸出結(jié)果:

  1. 開啟了2個(gè)新線程(異步執(zhí)行具備開啟線程的能力)
  2. 所有任務(wù)都在asyncConcurrent startasyncConcurrent end 之后執(zhí)行(異步執(zhí)行不會(huì)等待陵刹,繼續(xù)執(zhí)行任務(wù))
  3. 任務(wù)交替執(zhí)行(異步執(zhí)行具備開啟線程的能力,并且并行隊(duì)列可以開啟多個(gè)線程欢嘿,同時(shí)執(zhí)行多個(gè)任務(wù))
2018-12-27 11:18:23.174090+0800 Thread[1210:37289]  asyncConcurrent  start
2018-12-27 11:18:23.174253+0800 Thread[1210:37289]  asyncConcurrent  end
2018-12-27 11:18:23.174351+0800 Thread[1210:47038]  任務(wù)二衰琐,i = 0
2018-12-27 11:18:23.174401+0800 Thread[1210:37362]  任務(wù)一,i = 0
2018-12-27 11:18:24.177759+0800 Thread[1210:37362]  任務(wù)一际插,i = 1
2018-12-27 11:18:24.177759+0800 Thread[1210:47038]  任務(wù)二碘耳,i = 1
2018-12-27 11:18:25.178650+0800 Thread[1210:47038]  任務(wù)二,i = 2
2018-12-27 11:18:25.178650+0800 Thread[1210:37362]  任務(wù)一框弛,i = 2

3.2.5.同步執(zhí)行 + 主隊(duì)列

同步執(zhí)行 + 主隊(duì)列 在不同線程中調(diào)用辛辨,結(jié)果不一樣。
在主線程中調(diào)用瑟枫,出現(xiàn)死鎖
在其他線程中調(diào)用斗搞,不會(huì)開啟線程,一個(gè)任務(wù)執(zhí)行完畢后慷妙,再執(zhí)行下一個(gè)任務(wù)

3.2.5.1在主線程調(diào)用

/*
 同步執(zhí)行 + 主隊(duì)列
 在主線程中調(diào)用僻焚,出現(xiàn)死鎖
 */

- (void)syncMain{
    NSLog(@" syncMain  start");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_sync(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二膝擂,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    NSLog(@" syncMain  end");
}

輸出結(jié)果:
在Xcode10 上運(yùn)行崩潰虑啤,只輸出了syncMain start
為什么?
因?yàn)槲覀冊(cè)谥骶€程中執(zhí)行 syncMain方法架馋,相當(dāng)于把 syncMain任務(wù)放到主線程的隊(duì)列中狞山,而同步執(zhí)行會(huì)等待當(dāng)前隊(duì)列中的任務(wù)執(zhí)行完畢后,才會(huì)接著執(zhí)行叉寂。我們把任務(wù)一追加到主隊(duì)列中萍启,任務(wù)一會(huì)等待主線程處理完syncMain方法,而syncMain方法又需要等待任務(wù)一執(zhí)行完畢,才能繼續(xù)執(zhí)行勘纯,雙方都在等待局服,所以線程死鎖,任務(wù)無(wú)法執(zhí)行驳遵。

2018-12-27 11:26:17.011738+0800 Thread[1443:50531]  syncMain  start

3.2.5.2 在其他線程調(diào)用

/*
 同步執(zhí)行 + 主隊(duì)列
 在其他線程中調(diào)用淫奔,不會(huì)開啟線程,一個(gè)任務(wù)執(zhí)行完畢后堤结,再執(zhí)行下一個(gè)任務(wù)
 */

- (void)syncMain{
    NSLog(@"主線程");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@" syncMain  start");
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_sync(queue, ^{
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)一搏讶,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        });
        dispatch_sync(queue, ^{
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)二,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        });
        NSLog(@" syncMain  end");
    });
}

輸出結(jié)果:

  1. 沒(méi)有開啟線程霍殴,放在主隊(duì)列的任務(wù)都在主線程中執(zhí)行
  2. 所有任務(wù)都在syncMain startsyncMain end 之間執(zhí)行(同步執(zhí)行需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束)
  3. 任務(wù)按順序執(zhí)行(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接著一個(gè)執(zhí)行)
2018-12-27 14:22:03.964047+0800 Thread[2134:84333] 主線程
2018-12-27 14:22:03.964238+0800 Thread[2134:84385]  syncMain  start
2018-12-27 14:22:03.965009+0800 Thread[2134:84333]  任務(wù)一系吩,i = 0
2018-12-27 14:22:04.966453+0800 Thread[2134:84333]  任務(wù)一来庭,i = 1
2018-12-27 14:22:05.967903+0800 Thread[2134:84333]  任務(wù)一,i = 2
2018-12-27 14:22:06.968817+0800 Thread[2134:84333]  任務(wù)二穿挨,i = 0
2018-12-27 14:22:07.969877+0800 Thread[2134:84333]  任務(wù)二月弛,i = 1
2018-12-27 14:22:08.970376+0800 Thread[2134:84333]  任務(wù)二,i = 2
2018-12-27 14:22:09.971810+0800 Thread[2134:84385]  syncMain  end

3.2.6.異步執(zhí)行 + 主隊(duì)列

/*
 異步執(zhí)行 + 主隊(duì)列
 不會(huì)開啟新線程科盛,在主線程中執(zhí)行帽衙,一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)
 */

- (void)asyncMain{
    NSLog(@" asyncMain  start");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一贞绵,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二厉萝,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    NSLog(@" asyncMain  end");
}

輸出結(jié)果:

  1. 沒(méi)有開啟線程,放在主隊(duì)列的任務(wù)都在主線程中執(zhí)行
  2. 所有任務(wù)都在asyncMain startasyncMain end 之后執(zhí)行(異步執(zhí)行不會(huì)等待榨崩,繼續(xù)執(zhí)行任務(wù))
  3. 任務(wù)按順序執(zhí)行(因?yàn)橹麝?duì)列是串行隊(duì)列谴垫,每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接著一個(gè)執(zhí)行)
2018-12-27 14:33:15.293261+0800 Thread[2134:84333]  asyncMain  start
2018-12-27 14:33:15.293406+0800 Thread[2134:84333]  asyncMain  end
2018-12-27 14:33:15.293646+0800 Thread[2134:84333]  任務(wù)一母蛛,i = 0
2018-12-27 14:33:16.293875+0800 Thread[2134:84333]  任務(wù)一翩剪,i = 1
2018-12-27 14:33:17.295251+0800 Thread[2134:84333]  任務(wù)一,i = 2
2018-12-27 14:33:18.296751+0800 Thread[2134:84333]  任務(wù)二彩郊,i = 0
2018-12-27 14:33:19.297602+0800 Thread[2134:84333]  任務(wù)二前弯,i = 1
2018-12-27 14:33:20.298579+0800 Thread[2134:84333]  任務(wù)二,i = 2

4.線程間通信

在iOS開發(fā)工程中秫逝,我們一般在主線程中進(jìn)行UI刷新恕出,如:點(diǎn)擊、拖拽筷登、滾動(dòng)事件剃根,耗時(shí)操作放在其他線程中,而當(dāng)耗時(shí)操作結(jié)束后前方,回到主線程狈醉,就需要用到線程間的通信廉油。

在全局隊(duì)列中執(zhí)行任務(wù),任務(wù)完成后苗傅,切回主線程

- (void)gcdCommunication{
    NSLog(@"我在主線程");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一抒线,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"回到主線程");
        });
    });
}

輸出結(jié)果:

2018-12-27 15:05:55.188427+0800 Thread[2519:102865] 我在主線程
2018-12-27 15:05:55.188635+0800 Thread[2519:102899]  任務(wù)一,i = 0
2018-12-27 15:05:56.189689+0800 Thread[2519:102899]  任務(wù)一渣慕,i = 1
2018-12-27 15:05:57.191253+0800 Thread[2519:102899]  任務(wù)一嘶炭,i = 2
2018-12-27 15:05:58.195350+0800 Thread[2519:102865] 回到主線程

5.dispatch_once

創(chuàng)建單例或者整個(gè)程序只運(yùn)行一次的代碼,可以使用dispatch_once逊桦,dispatch_once函數(shù)保證這個(gè)程序運(yùn)行過(guò)程中只被執(zhí)行一次眨猎,即時(shí)在多線程情況下也是安全的。

/*
   驗(yàn)證dispatch_once
 */
- (void)testGcdOnce{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            [self gcdOnce];
        }
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            [self gcdOnce];
        }
    });
}

- (void)gcdOnce{
    NSLog(@"%s",__func__);
    static TicketManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[TicketManager alloc]init];
        NSLog(@"創(chuàng)建對(duì)象");
    });
}

輸出結(jié)果:dispatch_once里面只被執(zhí)行了一次

2018-12-27 15:19:31.228794+0800 Thread[2702:110251] -[ViewController gcdOnce]
2018-12-27 15:19:31.228794+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229290+0800 Thread[2702:110251] 創(chuàng)建對(duì)象
2018-12-27 15:19:31.229516+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229514+0800 Thread[2702:110251] -[ViewController gcdOnce]
2018-12-27 15:19:31.229630+0800 Thread[2702:110250] -[ViewController gcdOnce]
2018-12-27 15:19:31.229678+0800 Thread[2702:110251] -[ViewController gcdOnce]

6.dispatch_after

dispatch_after并不是在指定時(shí)間之后才執(zhí)行處理强经,而是在指定時(shí)間之后將任務(wù)追加到隊(duì)列中睡陪,這個(gè)指定時(shí)間并不是絕對(duì)準(zhǔn)確的,想要大致完成延時(shí)任務(wù)可以使用dispatch_after函數(shù)實(shí)現(xiàn)

- (void)gcdAfter{
    NSLog(@"%s",__func__);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%s",__func__);
    });;
}

輸出結(jié)果:大致為1秒

2018-12-27 15:28:23.416958+0800 Thread[2781:114534] -[ViewController gcdAfter]
2018-12-27 15:28:24.513064+0800 Thread[2781:114534] -[ViewController gcdAfter]_block_invoke

7. dispatch_apply(快速迭代)

dispatch_apply快速迭代方法匿情,按照指定次數(shù)將指定的任務(wù)添加到隊(duì)列中兰迫,并等待隊(duì)列中的任務(wù)全部執(zhí)行結(jié)束。

如果在串行隊(duì)列中使用dispatch_apply函數(shù)炬称,就和for循環(huán)遍歷一樣汁果,按照順序執(zhí)行,體現(xiàn)不出快速迭代的意義玲躯。

如果在并行隊(duì)列中使用dispatch_apply函數(shù)据德,dispatch_apply可以在多個(gè)線程中同時(shí)遍歷多個(gè)數(shù)字。

7.1 在串行隊(duì)列使用dispatch_apply

將在主隊(duì)列dispatch_get_main_queue的遍歷任務(wù)放在并行隊(duì)列dispatch_get_global_queue中跷车,為了避免上面講的死鎖問(wèn)題,關(guān)注apply beginapply end之間的代碼即可

/*
 dispatch_apply:快速迭代
 */
- (void)gcdApply{
    NSLog(@"主線程");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"apply begin");
        dispatch_apply(6, dispatch_get_main_queue(), ^(size_t index) {
            NSLog(@"index = %zu",index);
        });
        NSLog(@"apply end");
    });
   
}

輸出結(jié)果:

  1. 沒(méi)有開啟線程(主隊(duì)列的任務(wù)只在主線程中執(zhí)行)
  2. apply end在最后輸出(dispatch_apply函數(shù)會(huì)等待隊(duì)列全部任務(wù)執(zhí)行結(jié)束)
2018-12-27 15:58:28.788666+0800 Thread[3090:128936] 主線程
2018-12-27 15:58:28.788880+0800 Thread[3090:128988] apply begin
2018-12-27 15:58:28.819630+0800 Thread[3090:128936] index = 0
2018-12-27 15:58:28.819791+0800 Thread[3090:128936] index = 1
2018-12-27 15:58:28.819897+0800 Thread[3090:128936] index = 2
2018-12-27 15:58:28.820107+0800 Thread[3090:128936] index = 3
2018-12-27 15:58:28.820396+0800 Thread[3090:128936] index = 4
2018-12-27 15:58:28.820503+0800 Thread[3090:128936] index = 5
2018-12-27 15:58:28.820752+0800 Thread[3090:128988] apply end


7.2在并行隊(duì)列使用dispatch_apply

/*
 dispatch_apply:快速迭代
 */
- (void)gcdApply{
    NSLog(@"apply begin");
    dispatch_apply(6, dispatch_get_global_queue(0, 0), ^(size_t index) {
        NSLog(@"index = %zu",index);
    });
    NSLog(@"apply end");
}

輸出結(jié)果:

  1. 開啟線程(看線程編號(hào)得出)
  2. apply end在最后輸出(dispatch_apply函數(shù)會(huì)等待隊(duì)列全部任務(wù)執(zhí)行結(jié)束)
2018-12-27 15:39:06.219402+0800 Thread[2918:120358] apply begin
2018-12-27 15:39:06.219636+0800 Thread[2918:120391] index = 1
2018-12-27 15:39:06.219619+0800 Thread[2918:120358] index = 0
2018-12-27 15:39:06.219746+0800 Thread[2918:120391] index = 3
2018-12-27 15:39:06.219747+0800 Thread[2918:120392] index = 4
2018-12-27 15:39:06.219737+0800 Thread[2918:120358] index = 2
2018-12-27 15:39:06.219832+0800 Thread[2918:120391] index = 5
2018-12-27 15:39:06.219933+0800 Thread[2918:120358] apply end

需求1:我們需要異步執(zhí)行兩個(gè)操作(一個(gè)操作可以是一個(gè)任務(wù)晋控,也可以是多個(gè)任務(wù),這里是兩個(gè)任務(wù))姓赤,而且第一組操作結(jié)束后赡译,才能開始第二組操作,如何實(shí)現(xiàn)呢不铆?

這里的意思其實(shí)保持線程同步蝌焚,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)的意思。

方法1:使用柵欄函數(shù)
方法2:使用dispatch_group
方式3 :使用dispatch_semaphore

這三個(gè)方法都會(huì)在下面一一講解的誓斥。

總結(jié):就當(dāng)前這個(gè)需求只洒,使用柵欄函數(shù)會(huì)比較簡(jiǎn)單,所有方法講完具體實(shí)現(xiàn)就可以看出來(lái)了不用創(chuàng)建group或者semaphore,直接放個(gè)柵欄在中間劳坑,分隔兩個(gè)操作毕谴。

8.dispatch_barrier(柵欄函數(shù))

柵欄函數(shù)存在的意義:先執(zhí)行柵欄函數(shù)之前的任務(wù),再執(zhí)行柵欄函數(shù)中的任務(wù),最后執(zhí)行柵欄函數(shù)之后的任務(wù)涝开,增加?xùn)艡诤瘮?shù)不影響原有隊(duì)列的任務(wù)執(zhí)行方式循帐,也就是柵欄函數(shù)之前隊(duì)列的任務(wù)是什么執(zhí)行方式,柵欄函數(shù)之后隊(duì)列的任務(wù)還是什么執(zhí)行方式舀武。

柵欄函數(shù)分為:dispatch_barrier_asyncdispatch_barrier_sync
區(qū)別就是 :添加?xùn)艡诤瘮?shù)后面任務(wù)到隊(duì)列的時(shí)間不一樣拄养。

dispatch_barrier_sync需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束后,才會(huì)添加?xùn)艡诤瘮?shù)后的任務(wù)到隊(duì)列中银舱。

dispatch_barrier_async不需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束瘪匿,就已經(jīng)將柵欄函數(shù)后的任務(wù)到隊(duì)列中。

8.1 dispatch_barrier_sync

實(shí)現(xiàn)代碼

- (void)gcdBarrier{
    NSLog(@"主線程");
    dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一寻馏,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二棋弥,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    //1.dispatch_barrier_sync
    dispatch_barrier_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i ++) {
            NSLog(@" barrier1,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
        for (NSInteger i = 0; i < 3; i ++) {
            NSLog(@" barrier2诚欠,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
 
    NSLog(@"---------barrier代碼后面----------------");
    
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)三嘁锯,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)四,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
}

輸出結(jié)果:

  1. 執(zhí)行任務(wù)一聂薪、二,執(zhí)行柵欄函數(shù)中的barrier1蝗羊、barrier2藏澳,最后執(zhí)行任務(wù)三、四耀找;
  2. 任務(wù)一翔悠、二交替執(zhí)行(異步執(zhí)行),任務(wù)三野芒、四交替執(zhí)行(異步執(zhí)行)蓄愁,印證柵欄函數(shù)不影響隊(duì)列任務(wù)的執(zhí)行方式;
  3. 橫線在柵欄函數(shù)之后輸出(dispatch_barrier_sync:需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束后狞悲,才會(huì)添加?xùn)艡诤瘮?shù)后的任務(wù)到隊(duì)列中)撮抓。
2018-12-28 17:07:54.227141+0800 Thread[11766:169507] 主線程
2018-12-28 17:07:54.227455+0800 Thread[11766:169563]  任務(wù)二,i = 0
2018-12-28 17:07:54.227482+0800 Thread[11766:169565]  任務(wù)一摇锋,i = 0
2018-12-28 17:07:55.231426+0800 Thread[11766:169563]  任務(wù)二丹拯,i = 1
2018-12-28 17:07:55.231426+0800 Thread[11766:169565]  任務(wù)一,i = 1
2018-12-28 17:07:56.232952+0800 Thread[11766:169563]  任務(wù)二荸恕,i = 2
2018-12-28 17:07:56.232985+0800 Thread[11766:169565]  任務(wù)一乖酬,i = 2
2018-12-28 17:07:57.236980+0800 Thread[11766:169507]  barrier1,i = 0
2018-12-28 17:07:58.238423+0800 Thread[11766:169507]  barrier1融求,i = 1
2018-12-28 17:07:59.239776+0800 Thread[11766:169507]  barrier1咬像,i = 2
2018-12-28 17:08:00.240315+0800 Thread[11766:169507]  barrier2,i = 0
2018-12-28 17:08:01.240863+0800 Thread[11766:169507]  barrier2,i = 1
2018-12-28 17:08:02.242310+0800 Thread[11766:169507]  barrier2县昂,i = 2
2018-12-28 17:08:03.242826+0800 Thread[11766:169507] ---------barrier代碼后面----------------
2018-12-28 17:08:03.243200+0800 Thread[11766:169564]  任務(wù)三肮柜,i = 0
2018-12-28 17:08:03.243315+0800 Thread[11766:169663]  任務(wù)四,i = 0
2018-12-28 17:08:04.247765+0800 Thread[11766:169663]  任務(wù)四七芭,i = 1
2018-12-28 17:08:04.247765+0800 Thread[11766:169564]  任務(wù)三素挽,i = 1
2018-12-28 17:08:05.252405+0800 Thread[11766:169663]  任務(wù)四,i = 2
2018-12-28 17:08:05.252405+0800 Thread[11766:169564]  任務(wù)三狸驳,i = 2



8.2dispatch_barrier_async

實(shí)現(xiàn)代碼

 - (void)gcdBarrier{
    NSLog(@"主線程");
    dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一预明,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    //2.dispatch_barrier_async
    dispatch_barrier_async(queue, ^{
        for (NSInteger i = 0; i < 3; i ++) {
            NSLog(@" barrier1耙箍,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
        for (NSInteger i = 0; i < 3; i ++) {
            NSLog(@" barrier2撰糠,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    NSLog(@"---------barrier代碼后面----------------");
    
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)三,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)四辩昆,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
}


輸出結(jié)果:

  1. 執(zhí)行任務(wù)一阅酪、二,執(zhí)行柵欄函數(shù)中的barrier1汁针、barrier2术辐,最后執(zhí)行任務(wù)三、四施无;
  2. 任務(wù)一辉词、二交替執(zhí)行(異步執(zhí)行),任務(wù)三猾骡、四交替執(zhí)行(異步執(zhí)行)瑞躺,印證柵欄函數(shù)不影響隊(duì)列任務(wù)的執(zhí)行方式;
  3. 橫線在柵欄函數(shù)之前輸出(dispatch_barrier_async不需要等待柵欄函數(shù)中的任務(wù)執(zhí)行結(jié)束兴想,就已經(jīng)將柵欄函數(shù)后的任務(wù)到隊(duì)列中)幢哨。
2018-12-28 17:10:58.908322+0800 Thread[11798:171154] 主線程
2018-12-28 17:10:58.908586+0800 Thread[11798:171209]  任務(wù)一,i = 0
2018-12-28 17:10:58.908586+0800 Thread[11798:171154] ---------barrier代碼后面----------------
2018-12-28 17:10:58.908616+0800 Thread[11798:171207]  任務(wù)二嫂便,i = 0
2018-12-28 17:10:59.912811+0800 Thread[11798:171207]  任務(wù)二捞镰,i = 1
2018-12-28 17:10:59.912811+0800 Thread[11798:171209]  任務(wù)一,i = 1
2018-12-28 17:11:00.917006+0800 Thread[11798:171207]  任務(wù)二毙替,i = 2
2018-12-28 17:11:00.917047+0800 Thread[11798:171209]  任務(wù)一曼振,i = 2
2018-12-28 17:11:01.919238+0800 Thread[11798:171209]  barrier1,i = 0
2018-12-28 17:11:02.921574+0800 Thread[11798:171209]  barrier1蔚龙,i = 1
2018-12-28 17:11:03.924472+0800 Thread[11798:171209]  barrier1冰评,i = 2
2018-12-28 17:11:04.929280+0800 Thread[11798:171209]  barrier2,i = 0
2018-12-28 17:11:05.930060+0800 Thread[11798:171209]  barrier2木羹,i = 1
2018-12-28 17:11:06.931831+0800 Thread[11798:171209]  barrier2甲雅,i = 2
2018-12-28 17:11:07.934022+0800 Thread[11798:171209]  任務(wù)三解孙,i = 0
2018-12-28 17:11:07.934022+0800 Thread[11798:171207]  任務(wù)四,i = 0
2018-12-28 17:11:08.939572+0800 Thread[11798:171207]  任務(wù)四抛人,i = 1
2018-12-28 17:11:08.939573+0800 Thread[11798:171209]  任務(wù)三弛姜,i = 1
2018-12-28 17:11:09.942792+0800 Thread[11798:171209]  任務(wù)三,i = 2
2018-12-28 17:11:09.942829+0800 Thread[11798:171207]  任務(wù)四妖枚,i = 2


9.dispatch_group(隊(duì)列組)

任務(wù)的執(zhí)行是先創(chuàng)建一個(gè)任務(wù)廷臼,放入隊(duì)列進(jìn)行執(zhí)行。而隊(duì)列組就是用來(lái)存放隊(duì)列的一個(gè)組绝页。

將隊(duì)列放入隊(duì)列組中可以使用 dispatch_group_async 或者dispatch_group_enterdispatch_group_leave的組合實(shí)現(xiàn)荠商。

隊(duì)列組的任務(wù)結(jié)束完成后,調(diào)用dispatch_group_notify 可以回到指定線程執(zhí)行任務(wù)续誉,調(diào)用dispatch_group_wait可以回到當(dāng)前線程執(zhí)行任務(wù)(阻塞當(dāng)前線程)莱没。

就上面的需求1,用dispatch_group方式實(shí)現(xiàn)

1. 我們用dispatch_group_async + dispatch_group_notify來(lái)進(jìn)行實(shí)現(xiàn)

- (void)requirementGroupAsync{
    NSLog(@"主線程");
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)一酷鸦,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        
    });
    dispatch_group_async(group, queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二饰躲,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"------dispatch_group_notify------");
        dispatch_async(queue, ^{
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)三,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        });
        dispatch_async(queue, ^{
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)四臼隔,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        });
        
    });
}

輸出結(jié)果:先執(zhí)行任務(wù)一嘹裂、二,再執(zhí)行任務(wù)三摔握、四寄狼;

2018-12-28 17:59:23.695495+0800 Thread[12227:189820] 主線程
2018-12-28 17:59:23.695775+0800 Thread[12227:189867]  任務(wù)一,i = 0
2018-12-28 17:59:23.695778+0800 Thread[12227:189868]  任務(wù)二盒发,i = 0
2018-12-28 17:59:24.697513+0800 Thread[12227:189867]  任務(wù)一,i = 1
2018-12-28 17:59:24.697514+0800 Thread[12227:189868]  任務(wù)二狡逢,i = 1
2018-12-28 17:59:25.701243+0800 Thread[12227:189868]  任務(wù)二宁舰,i = 2
2018-12-28 17:59:25.701243+0800 Thread[12227:189867]  任務(wù)一,i = 2
2018-12-28 17:59:26.705407+0800 Thread[12227:189868] ------dispatch_group_notify------
2018-12-28 17:59:26.705799+0800 Thread[12227:189868]  任務(wù)三奢浑,i = 0
2018-12-28 17:59:26.705803+0800 Thread[12227:189866]  任務(wù)四蛮艰,i = 0
2018-12-28 17:59:27.709876+0800 Thread[12227:189868]  任務(wù)三,i = 1
2018-12-28 17:59:27.709873+0800 Thread[12227:189866]  任務(wù)四雀彼,i = 1
2018-12-28 17:59:28.713711+0800 Thread[12227:189866]  任務(wù)四壤蚜,i = 2
2018-12-28 17:59:28.713711+0800 Thread[12227:189868]  任務(wù)三,i = 2

2. 我們用dispatch_group_async + dispatch_group_wait來(lái)進(jìn)行實(shí)現(xiàn)

- (void)requirementGroupAsync{
    NSLog(@"主線程");
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)一徊哑,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        
    });
    dispatch_group_async(group, queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二袜刷,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    //2.dispatch_group_wait
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"-----dispatch_group_wait----");
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)三,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)四莺丑,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    
}

輸出結(jié)果:

  1. 先執(zhí)行任務(wù)一著蟹、二墩蔓,再執(zhí)行任務(wù)三、四萧豆;
  2. dispatch_group_wait在主線程輸出奸披,就如之前所說(shuō) dispatch_group_wait會(huì)阻塞當(dāng)前線程。
2018-12-28 18:07:54.379565+0800 Thread[12300:193286] 主線程
2018-12-28 18:07:54.379782+0800 Thread[12300:193334]  任務(wù)二涮雷,i = 0
2018-12-28 18:07:54.379783+0800 Thread[12300:193332]  任務(wù)一阵面,i = 0
2018-12-28 18:07:55.384688+0800 Thread[12300:193334]  任務(wù)二,i = 1
2018-12-28 18:07:55.384720+0800 Thread[12300:193332]  任務(wù)一洪鸭,i = 1
2018-12-28 18:07:56.385807+0800 Thread[12300:193332]  任務(wù)一样刷,i = 2
2018-12-28 18:07:56.385808+0800 Thread[12300:193334]  任務(wù)二,i = 2
2018-12-28 18:07:57.386847+0800 Thread[12300:193286] -----dispatch_group_wait----
2018-12-28 18:07:57.387161+0800 Thread[12300:193334]  任務(wù)三卿嘲,i = 0
2018-12-28 18:07:57.387174+0800 Thread[12300:193332]  任務(wù)四颂斜,i = 0
2018-12-28 18:07:58.387806+0800 Thread[12300:193332]  任務(wù)四,i = 1
2018-12-28 18:07:58.387808+0800 Thread[12300:193334]  任務(wù)三拾枣,i = 1
2018-12-28 18:07:59.390173+0800 Thread[12300:193332]  任務(wù)四沃疮,i = 2
2018-12-28 18:07:59.390209+0800 Thread[12300:193334]  任務(wù)三,i = 2

3. 我們用dispatch_group_enter + dispatch_group_leave + dispatch_group_notify來(lái)進(jìn)行實(shí)現(xiàn)

- (void)requirementGroupEnter{
    NSLog(@"主線程");
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一梅肤,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二司蔬,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
        dispatch_group_leave(group);
    });

    //1.dispatch_group_notify
    dispatch_group_notify(group, queue, ^{
        NSLog(@"------dispatch_group_notify------");
        dispatch_async(queue, ^{
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)三左医,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        });
        dispatch_async(queue, ^{
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)四,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        });
        
    });
}

輸出結(jié)果:先執(zhí)行任務(wù)一芥映、二奈偏,再執(zhí)行任務(wù)三唁盏、四;

2018-12-28 18:19:52.294127+0800 Thread[12422:198467] 主線程
2018-12-28 18:19:52.294429+0800 Thread[12422:198501]  任務(wù)一刽严,i = 0
2018-12-28 18:19:52.294438+0800 Thread[12422:198504]  任務(wù)二撑螺,i = 0
2018-12-28 18:19:53.297632+0800 Thread[12422:198504]  任務(wù)二主胧,i = 1
2018-12-28 18:19:53.297642+0800 Thread[12422:198501]  任務(wù)一,i = 1
2018-12-28 18:19:54.302891+0800 Thread[12422:198501]  任務(wù)一诀黍,i = 2
2018-12-28 18:19:54.302891+0800 Thread[12422:198504]  任務(wù)二歇拆,i = 2
2018-12-28 18:19:55.307933+0800 Thread[12422:198501] ------dispatch_group_notify------
2018-12-28 18:19:55.308242+0800 Thread[12422:198501]  任務(wù)三渠啊,i = 0
2018-12-28 18:19:55.308258+0800 Thread[12422:198502]  任務(wù)四,i = 0
2018-12-28 18:19:56.312331+0800 Thread[12422:198501]  任務(wù)三权旷,i = 1
2018-12-28 18:19:56.312331+0800 Thread[12422:198502]  任務(wù)四替蛉,i = 1
2018-12-28 18:19:57.313584+0800 Thread[12422:198502]  任務(wù)四,i = 2
2018-12-28 18:19:57.313584+0800 Thread[12422:198501]  任務(wù)三拄氯,i = 2

4. 我們用dispatch_group_enter + dispatch_group_leave + dispatch_group_wait來(lái)進(jìn)行實(shí)現(xiàn)

- (void)requirementGroupEnter{
    NSLog(@"主線程");
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一躲查,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)二,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
        dispatch_group_leave(group);
    });
//    2.dispatch_group_wait
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        NSLog(@"-----dispatch_group_wait----");
        dispatch_async(queue, ^{
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)三译柏,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        });
        dispatch_async(queue, ^{
            for (NSInteger  i = 0; i < 3; i ++) {
                NSLog(@" 任務(wù)四镣煮,i = %ld",(long)i);
                [NSThread sleepForTimeInterval:1.0];
            }
        });
    
}

  1. 先執(zhí)行任務(wù)一、二鄙麦,再執(zhí)行任務(wù)三典唇、四;
  2. dispatch_group_wait在主線程輸出黔衡,就如之前所說(shuō) dispatch_group_wait會(huì)阻塞當(dāng)前線程
2018-12-28 18:21:29.197243+0800 Thread[12446:199547] 主線程
2018-12-28 18:21:29.197534+0800 Thread[12446:199590]  任務(wù)一蚓聘,i = 0
2018-12-28 18:21:29.197598+0800 Thread[12446:199591]  任務(wù)二,i = 0
2018-12-28 18:21:30.200867+0800 Thread[12446:199591]  任務(wù)二盟劫,i = 1
2018-12-28 18:21:30.200870+0800 Thread[12446:199590]  任務(wù)一夜牡,i = 1
2018-12-28 18:21:31.204217+0800 Thread[12446:199590]  任務(wù)一,i = 2
2018-12-28 18:21:31.204215+0800 Thread[12446:199591]  任務(wù)二侣签,i = 2
2018-12-28 18:21:32.205551+0800 Thread[12446:199547] -----dispatch_group_wait----
2018-12-28 18:21:32.205926+0800 Thread[12446:199590]  任務(wù)三塘装,i = 0
2018-12-28 18:21:32.205930+0800 Thread[12446:199591]  任務(wù)四,i = 0
2018-12-28 18:21:33.210243+0800 Thread[12446:199590]  任務(wù)三影所,i = 1
2018-12-28 18:21:33.210413+0800 Thread[12446:199591]  任務(wù)四蹦肴,i = 1
2018-12-28 18:21:34.215728+0800 Thread[12446:199590]  任務(wù)三,i = 2
2018-12-28 18:21:34.215728+0800 Thread[12446:199591]  任務(wù)四猴娩,i = 2

dispatch_group_asyncdispatch_group_enter + dispatch_group_leave有什么區(qū)別呢阴幌?

需求2:異步執(zhí)行兩個(gè)網(wǎng)絡(luò)請(qǐng)求,兩個(gè)網(wǎng)絡(luò)請(qǐng)求執(zhí)行結(jié)束后卷中,進(jìn)行一定的操作矛双。

這里sendRequest用來(lái)代表網(wǎng)絡(luò)請(qǐng)求

- (void)sendRequest:(void (^)(void))block{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"start task 1");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"end task 1");
        dispatch_async(dispatch_get_main_queue(), ^{
            if (block) {
                block();
            }
        });
    });
}

- (void)sendRequest2:(void (^)(void))block{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"start task 2");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"end task 2");
        dispatch_async(dispatch_get_main_queue(), ^{
            if (block) {
                block();
            }
        });
    });
}

1. 用dispatch_group_async實(shí)現(xiàn)

- (void)gcdGroupAsync{
    NSLog(@"主線程");
//    dispatch_group_async 里面,應(yīng)該放同步代碼蟆豫,而不是異步代碼
        dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
        dispatch_group_t group = dispatch_group_create();
        dispatch_group_async(group, queue, ^{
    
            [self sendRequest:^{
                NSLog(@"sendRequest done");
            }];
        });
    
        dispatch_group_async(group, queue, ^{
            [self sendRequest2:^{
                NSLog(@"sendRequest2 done");
            }];
        });
        dispatch_group_notify(group, queue, ^{
            NSLog(@"all task over");
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程刷新UI");
            });
        });
}

輸出結(jié)果:顯示不符合需求议忽,task1和task2還沒(méi)有結(jié)束,就輸出all task over 咯十减,為啥子呢栈幸?
原因:因?yàn)?code>dispatch_group_async里面放入的是異步的任務(wù)愤估,dispatch_group_async執(zhí)行了sendRequest這行代碼后,就認(rèn)為sendRequest已經(jīng)執(zhí)行完畢了(其實(shí)還沒(méi)有回調(diào)回來(lái))速址,group不再持有這個(gè)任務(wù)玩焰,就會(huì)執(zhí)行下面的dispatch_group_async,而sendRequest2同理芍锚,group沒(méi)有任務(wù)時(shí)震捣,就會(huì)執(zhí)行dispatch_group_notify 里面的任務(wù),所以造成這樣子的輸出結(jié)果闹炉。

由此可見(jiàn):dispatch_group_async里面適合放入同步代碼蒿赢,而不是異步代碼。

2018-12-29 17:13:42.710421+0800 Thread[17395:417245] 主線程
2018-12-29 17:13:42.710700+0800 Thread[17395:417334] start task 2
2018-12-29 17:13:42.710726+0800 Thread[17395:417333] all task over
2018-12-29 17:13:42.710708+0800 Thread[17395:417331] start task 1
2018-12-29 17:13:42.733662+0800 Thread[17395:417245] 回到主線程刷新UI
2018-12-29 17:13:45.714431+0800 Thread[17395:417331] end task 1
2018-12-29 17:13:45.714511+0800 Thread[17395:417334] end task 2
2018-12-29 17:13:45.714752+0800 Thread[17395:417245] sendRequest done
2018-12-29 17:13:45.714854+0800 Thread[17395:417245] sendRequest2 done

2.用dispatch_group_enter + dispatch_group_leave 實(shí)現(xiàn)

- (void)gcdGroupEnter{
    NSLog(@"主線程");
  
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.group", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    [self sendRequest:^{
        NSLog(@"sendRequest done");
        dispatch_group_leave(group);
    }];
    
    dispatch_group_enter(group);
    [self sendRequest2:^{
        NSLog(@"sendRequest2 done");
        dispatch_group_leave(group);
    }];
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"all task over");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"回到主線程刷新UI");
        });
    });
}

輸出結(jié)果:當(dāng)task1 和task2執(zhí)行結(jié)束后渣触,才輸出all task over羡棵,符合我們的需求。
當(dāng)我們調(diào)用sendRequest時(shí)嗅钻,先調(diào)用dispatch_group_enter時(shí)皂冰,任務(wù)回調(diào)后再調(diào)用dispatch_group_leave,整個(gè)異步操作中养篓,任務(wù)是被group持有的秃流,只有回調(diào)結(jié)束后才離開group,所以不會(huì)出現(xiàn)上面的問(wèn)題柳弄。

注意:dispatch_group_enterdispatch_group_leave成對(duì)存在

2018-12-29 17:32:12.739361+0800 Thread[17557:425760] 主線程
2018-12-29 17:32:12.739608+0800 Thread[17557:425809] start task 1
2018-12-29 17:32:12.739608+0800 Thread[17557:425810] start task 2
2018-12-29 17:32:15.742987+0800 Thread[17557:425809] end task 1
2018-12-29 17:32:15.742990+0800 Thread[17557:425810] end task 2
2018-12-29 17:32:15.743325+0800 Thread[17557:425760] sendRequest2 done
2018-12-29 17:32:15.743528+0800 Thread[17557:425760] sendRequest done
2018-12-29 17:32:15.743742+0800 Thread[17557:425810] all task over
2018-12-29 17:32:15.744000+0800 Thread[17557:425760] 回到主線程刷新UI


10.dispatch_semaphore(信號(hào)量)

dispatch_semaphore 使用計(jì)數(shù)來(lái)完成這個(gè)問(wèn)題舶胀,計(jì)數(shù)為0 ,不可以通過(guò)碧注,計(jì)數(shù)大于等于1嚣伐,可以通過(guò)
其中有三個(gè)函數(shù)分別為:

  1. dispatch_semaphore_create :創(chuàng)建semaphore并初始化信號(hào)量,初始化的值大于等于0;
  2. dispatch_semaphore_signal:信號(hào)量+1萍丐;
  3. dispatch_semaphore_wait : 判斷當(dāng)前信號(hào)量的值轩端,如果當(dāng)前信號(hào)量大于0,信號(hào)量-1逝变,往下執(zhí)行基茵,如果當(dāng)前信號(hào)量等于0,就會(huì)阻塞在當(dāng)前線程壳影,一直等待拱层。

用處:

1、保持線程同步态贤,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)
2舱呻、保證線程安全醋火,為線程加鎖

線程安全:在多個(gè)線程中同時(shí)訪問(wèn)并操作同一對(duì)象時(shí)悠汽,運(yùn)行結(jié)果與預(yù)期的值相同就是線程安全箱吕。
線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的,若每個(gè)線程中對(duì)全局變量柿冲、靜態(tài)變量只有讀操作茬高,而無(wú)寫操作,一般來(lái)說(shuō)假抄,這個(gè)全局變量是線程安全的怎栽;若有多個(gè)線程同時(shí)執(zhí)行寫操作,一般都需要考慮線程同步宿饱,否則的話就可能影響線程安全熏瞄。

線程同步:可理解為線程A和B一塊配合,A執(zhí)行到一定程度時(shí)要依靠B的某個(gè)結(jié)果谬以,于是停下來(lái)强饮,示意B運(yùn)行;B依言執(zhí)行为黎,再將結(jié)果給A邮丰;A再繼續(xù)操作。

10.1.保持線程同步铭乾,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)

這個(gè)也可以用來(lái)實(shí)現(xiàn)上面的需求1

- (void)gcdSemaphore{
    NSLog(@"主線程");
    dispatch_queue_t queue = dispatch_queue_create("com.xiuxiu.gcd", DISPATCH_QUEUE_CONCURRENT);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)一剪廉,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
        long x = dispatch_semaphore_signal(semaphore);
        NSLog(@"signal后的信號(hào)量 = %ld",x);
    });
    long x = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"wait后的信號(hào)量 = %ld",x);
    NSLog(@"---dispatch_semaphore_wait-----");
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)三,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger  i = 0; i < 3; i ++) {
            NSLog(@" 任務(wù)四炕檩,i = %ld",(long)i);
            [NSThread sleepForTimeInterval:1.0];
        }
    });
}

輸出結(jié)果:

  1. 先輸出任務(wù)一斗蒋,再輸出任務(wù)三、四
  2. dispatch_semaphore_wait在主線程輸出(信號(hào)量為0 笛质,阻塞在當(dāng)前線程)

我們?cè)谥骶€程創(chuàng)建了一個(gè)信號(hào)量賦值為0吹泡,并開辟了一個(gè)并行隊(duì)列異步執(zhí)行任務(wù)一,因?yàn)槭且粋€(gè)異步操作经瓷,此時(shí)不會(huì)等待任務(wù)一執(zhí)行結(jié)束爆哑, 直接執(zhí)行到dispatch_semaphore_wait,此時(shí)判斷出信號(hào)量的值0舆吮,不可通行揭朝,阻塞當(dāng)前線程,當(dāng)運(yùn)行到dispatch_semaphore_signal時(shí)色冀,信號(hào)量加1后等于1大于0潭袱,可通行,執(zhí)行dispatch_semaphore_wait后面的任務(wù)三锋恬、四屯换,通行后信號(hào)量減1等于0。

2018-12-29 15:22:24.960944+0800 Thread[16315:368237] 主線程
2018-12-29 15:22:24.961180+0800 Thread[16315:368286]  任務(wù)一,i = 0
2018-12-29 15:22:25.965568+0800 Thread[16315:368286]  任務(wù)一彤悔,i = 1
2018-12-29 15:22:26.970160+0800 Thread[16315:368286]  任務(wù)一嘉抓,i = 2
2018-12-29 15:22:27.975627+0800 Thread[16315:368237] wait后的信號(hào)量 = 0
2018-12-29 15:22:27.975627+0800 Thread[16315:368286] signal后的信號(hào)量 = 1
2018-12-29 15:22:27.975956+0800 Thread[16315:368237] ---dispatch_semaphore_wait-----
2018-12-29 15:22:27.976232+0800 Thread[16315:368287]  任務(wù)四,i = 0
2018-12-29 15:22:27.976285+0800 Thread[16315:368286]  任務(wù)三晕窑,i = 0
2018-12-29 15:22:28.978431+0800 Thread[16315:368286]  任務(wù)三抑片,i = 1
2018-12-29 15:22:28.978445+0800 Thread[16315:368287]  任務(wù)四,i = 1
2018-12-29 15:22:29.980405+0800 Thread[16315:368286]  任務(wù)三杨赤,i = 2
2018-12-29 15:22:29.980405+0800 Thread[16315:368287]  任務(wù)四敞斋,i = 2

10.2.保證線程安全,為線程加鎖

在上面講NSThread的時(shí)候疾牲,講過(guò)synchronizedNSCondition加鎖方式植捎,這里使用dispatch_semaphore進(jìn)行加密,運(yùn)行結(jié)果和上面一致。

- (void)sale{
    while (1) {
        //1阳柔、synchronized
//        @synchronized (self) {
//            if (self.tickets > 0 ) {
//                [NSThread sleepForTimeInterval:0.1];
//                self.tickets --;
//                self.saleCount = Total - self.tickets;
//                NSLog(@"%@ , 賣出 = %d鸥跟,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
//            }else{
//                break;//一定要break,不然就會(huì)死循環(huán)
//            }
//        }
//        2盔沫、NSCondition
//        [self.condition lock];
//        if (self.tickets > 0 ) {
//            [NSThread sleepForTimeInterval:0.1];
//            self.tickets --;
//            self.saleCount = Total - self.tickets;
//            NSLog(@"%@ , 賣出 = %d医咨,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
//        }else{
//            break;
//        }
//        [self.condition unlock];
//
        //3、dispatch_semaphore方式
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        if (self.tickets > 0 ) {
            [NSThread sleepForTimeInterval:0.1];
            self.tickets --;
            self.saleCount = Total - self.tickets;
            NSLog(@"%@ , 賣出 = %d架诞,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
        }else{
            dispatch_semaphore_signal(self.semaphore);
            break;
        }
        dispatch_semaphore_signal(self.semaphore);
    }
}

信號(hào)量還需要多看點(diǎn)資料拟淮,這里就先這樣子吧~~

上面就保持線程同步,將異步任務(wù)轉(zhuǎn)化為同步任務(wù)谴忧,保證線程安全很泊,給線程加鎖就講了好多種方式,選擇的時(shí)候沾谓,針對(duì)需求而言來(lái)選擇一個(gè)較好的方式就OK啦~

參考博客:

iOS多線程慕課網(wǎng)視頻
深入理解iOS開發(fā)中的鎖

文章鏈接:
iOS 多線程- pThread和NSThread
iOS 多線程-NSOperation + NSOperationQueue

喜歡就點(diǎn)個(gè)贊吧????
有錯(cuò)之處委造,還請(qǐng)指出,感謝????

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末均驶,一起剝皮案震驚了整個(gè)濱河市昏兆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌妇穴,老刑警劉巖爬虱,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異腾它,居然都是意外死亡跑筝,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門瞒滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)曲梗,“玉大人,你說(shuō)我怎么就攤上這事÷擦剑” “怎么了愧旦?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)碘举。 經(jīng)常有香客問(wèn)我,道長(zhǎng)搁廓,這世上最難降的妖魔是什么引颈? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮境蜕,結(jié)果婚禮上蝙场,老公的妹妹穿的比我還像新娘。我一直安慰自己粱年,他們只是感情好售滤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著台诗,像睡著了一般完箩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拉队,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天弊知,我揣著相機(jī)與錄音,去河邊找鬼粱快。 笑死秩彤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的事哭。 我是一名探鬼主播漫雷,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鳍咱!你這毒婦竟也來(lái)了降盹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谤辜,失蹤者是張志新(化名)和其女友劉穎澎现,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體每辟,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剑辫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渠欺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妹蔽。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胳岂,到底是詐尸還是另有隱情编整,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布乳丰,位于F島的核電站掌测,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏产园。R本人自食惡果不足惜汞斧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望什燕。 院中可真熱鬧粘勒,春花似錦、人聲如沸屎即。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)技俐。三九已至乘陪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雕擂,已是汗流浹背暂刘。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捂刺,地道東北人谣拣。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像族展,于是被迫代替她去往敵國(guó)和親森缠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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