GCD相關(guān)學(xué)習(xí)整理

GCD多線程整理學(xué)習(xí)

GCD簡介

iOS中多核編程的解決方法。

主要用于優(yōu)化應(yīng)用程序以及支持多核處理器以及其他對稱多處理系統(tǒng)。iOS4推出

優(yōu)點

<pre>

1.可用于多核并行運算

2.自動利用更多CPU內(nèi)核(雙核罚斗,四核等)

3.GCD會自動管理線程生命周期(線程的創(chuàng)建和銷毀副渴、調(diào)度任務(wù))

4.不需要編寫線程管理代碼

</pre>

GCD 任務(wù)和隊列

任務(wù)

就是執(zhí)行操作的意思,也就是線程中執(zhí)行的代碼

<p>

執(zhí)行代碼又分:同步執(zhí)行筷弦、異步執(zhí)行

</p>

  • 同步執(zhí)行

    • 同步添加任務(wù)到指定隊列中溅潜,在添加的任務(wù)執(zhí)行結(jié)束之前术唬,會一直等待,直到隊列里面的任務(wù)完成之后再繼續(xù)執(zhí)行

    • 只能在當(dāng)前線程中執(zhí)行任務(wù)滚澜,不具備開啟新線程的能力

  • 異步執(zhí)行

    • 異步添加任務(wù)到指定隊列中粗仓,它不會做任何等待,可以繼續(xù)執(zhí)行任務(wù)

    • 可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力

注意:異步執(zhí)行(asyn)雖然具有開啟線程的能力借浊,但是并不一定開啟新線程塘淑。這跟任務(wù)所指定的隊列類型有關(guān)。

隊列(Dispatch Queue)

執(zhí)行任務(wù)的等待隊列蚂斤,屬于特殊的線性列存捺,F(xiàn)IFO原則如下圖

隊列圖

隊列又分串行隊列并發(fā)隊列,兩者符合FIFO曙蒸,區(qū)別在于:執(zhí)行順序不同捌治,開啟線程數(shù)不同

  • 串行隊列(Serial Dispatch Queue)

    • 任務(wù)逐一執(zhí)行,一個完成才到下一個

    • 只開啟一個新線程

  • 并發(fā)隊列(Concurrent Dispatch Queue)

    • 可以讓多任務(wù)并發(fā)(同時)執(zhí)行

    • 可以開啟多個線程纽窟,并同時執(zhí)行任務(wù)

注意:并發(fā)隊列的并發(fā)功能只有異步(dispatch_asyn)函數(shù)下有效

區(qū)別如圖

串行隊列
并發(fā)隊列

GCD使用

使用步驟:
  1. 創(chuàng)建隊列(串行肖油、并發(fā))

  2. 將任務(wù)追加到隊列中,系統(tǒng)會根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或異步執(zhí)行)

隊列創(chuàng)建/獲取

  • 使用 dispatch_queue_create 創(chuàng)建

//串行隊列的創(chuàng)建方法

  dispatch_queue_t queue = dispatch_queue_create("key", DISPATCH_QUEUE_SERIAL);

// 并發(fā)隊列的創(chuàng)建方法

dispatch_queue_t queue = dispatch_queue_create("key", DISPATCH_QUEUE_CONCURRENT);

  • 對于串行隊列师倔,特殊的串行隊列,主隊列 Main Dispatch Queue

    • 所有主隊列任務(wù)都會在主線程執(zhí)行

    • 使用 dispatch_get_main_queue()獲取主隊列

  • 對于并發(fā)隊列周蹭,GCD的默認(rèn)全局并發(fā)隊列 Global Dispatch Queue

    • 使用dispatch_get_global_queue獲取,第一個參數(shù)趋艘,優(yōu)先級
    
    

// 全局并發(fā)隊列的獲取方法

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);




####任務(wù)的創(chuàng)建方法

1. 同步執(zhí)行任務(wù):`dispatch_sync`

2. 異步執(zhí)行任務(wù):`dispatch_async`

// 同步執(zhí)行任務(wù)創(chuàng)建方法

dispatch_sync(queue, ^{

// 這里放同步執(zhí)行任務(wù)代碼

});

// 異步執(zhí)行任務(wù)創(chuàng)建方法

dispatch_async(queue, ^{

// 這里放異步執(zhí)行任務(wù)代碼

});


根據(jù)隊列可分為四種情況

> 1. 同步執(zhí)行+并發(fā)隊列

> 2. 異步執(zhí)行+并發(fā)隊列

> 3. 同步執(zhí)行+串行隊列

> 4. 異步執(zhí)行+串行隊列

對于之前的兩個特殊隊列,全局并發(fā)隊列凶朗、主隊列瓷胧。全局并發(fā)隊列可以作為普通并發(fā)隊列使用,但是主隊列有點特殊棚愤,故又多了兩種組合

> 5. 同步執(zhí)行+主隊列

> 6. 異步執(zhí)行+主隊列

總結(jié):

區(qū)別 | 并發(fā)隊列 | 串行隊列 | 主隊列

:--| :-- | :-- | :--

同步(sync)|不開新線程搓萧,串行|不開新線程,串行|主線程調(diào)用:死鎖卡死宛畦;<p>其他線程:不開新線程瘸洛,串行

異步(async)|有開新線程,并發(fā)|有開新線程(1條)次和,串行|不開新線程反肋,串行

###GCD的基本使用

####1.同步執(zhí)行 + 并發(fā)隊列

* 在當(dāng)前線程中執(zhí)行任務(wù),不會開啟新線程踏施,執(zhí)行完一個任務(wù)石蔗,再執(zhí)行下一個任務(wù),F(xiàn)IFO原則

/**

  • 同步執(zhí)行 + 并發(fā)隊列

  • 特點:在當(dāng)前線程中執(zhí)行任務(wù)畅形,不會開啟新線程养距,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)日熬。

*/

  • (void)syncConcurrent {

    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程

    NSLog(@"syncConcurrent---begin");

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);



dispatch_sync(queue, ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_sync(queue, ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_sync(queue, ^{

    // 追加任務(wù)3

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



NSLog(@"syncConcurrent---end");

}


輸出結(jié)果:

> <pre>

currentThread—-{number = 1, name = main}

syncConcurrent—-begin

1—-{number = 1, name = main}

1—-{number = 1, name = main}

2—-{number = 1, name = main}

2—-{number = 1, name = main}

3—-{number = 1, name = main}

3—-{number = 1, name = main}

syncConcurrent—-end

</pre>

結(jié)論:

1. 所有任務(wù)都在當(dāng)前線程執(zhí)行棍厌,沒有開啟新線程(`同步執(zhí)行`不具備開啟新線程的能力)

2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之間執(zhí)行(`同步任務(wù)`需要等待隊列的任務(wù)執(zhí)行結(jié)束)

3. 任務(wù)按順序執(zhí)行的。原因:雖然`并發(fā)隊列`可以開啟多個線程,并且同時執(zhí)行多個任務(wù)定铜,但是因為本身不能創(chuàng)建新線程阳液,只有當(dāng)前線程這一個線程(`同步任務(wù)`不具備開啟新線程的能力),所以也就不存在并發(fā)揣炕。而且當(dāng)前線程只有等待當(dāng)前隊列中正在執(zhí)行的任務(wù)執(zhí)行完畢之后才能繼續(xù)接著執(zhí)行下面的操作(`同步任務(wù)`需要等待隊列的任務(wù)執(zhí)行結(jié)束)帘皿。所以任務(wù)只能一個接著一個按順序執(zhí)行,不能同時被執(zhí)行

>綜上所述畸陡,`同步執(zhí)行`無開新線程能力鹰溜,導(dǎo)致就算是并發(fā)隊列也不能只能在當(dāng)前線程執(zhí)行,從而導(dǎo)致只能串行執(zhí)行任務(wù)丁恭,不具備并發(fā)能力

####2.異步執(zhí)行 + 并發(fā)隊列

* 可以開啟多個線程曹动,任務(wù)交替(同時執(zhí)行)

/**

  • 異步執(zhí)行 + 并發(fā)隊列

  • 特點:可以開啟多個線程,任務(wù)交替(同時)執(zhí)行牲览。

*/

  • (void)asyncConcurrent {

    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程

    NSLog(@"asyncConcurrent---begin");

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);



dispatch_async(queue, ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_async(queue, ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_async(queue, ^{

    // 追加任務(wù)3

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



NSLog(@"asyncConcurrent---end");

}


輸出結(jié)果

><pre>

currentThread—-{number = 1, name = main}

asyncConcurrent—-begin

asyncConcurrent—-end

2—-{number = 5, name = (null)}

3—-{number = 4, name = (null)}

1—-{number = 3, name = (null)}

3—-{number = 4, name = (null)}

1—-{number = 3, name = (null)}

2—-{number = 5, name = (null)}

</pre>

結(jié)論:

1. 除了當(dāng)前線程墓陈,系統(tǒng)又開了3個線程,并且任務(wù)交替/同時執(zhí)行第献。(`異步執(zhí)行`具備開啟新線程的能力贡必,且`并發(fā)隊列`可開啟多個線程,同時執(zhí)行多個任務(wù))

2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之后才執(zhí)行的庸毫。說明當(dāng)前線程沒有等待仔拟,而是直接開啟了新線程,在新線程中執(zhí)行任務(wù)(`異步執(zhí)行`不做等待飒赃,可以繼續(xù)執(zhí)行任務(wù))

>綜上所述利花,`異步執(zhí)行`有開啟新線程能力,所以并發(fā)隊列才能開啟多個線程载佳,`異步執(zhí)行`并不會等待炒事,而是繼續(xù)執(zhí)行當(dāng)前任務(wù)

####3.同步執(zhí)行 + 串行隊列

* 不會開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)蔫慧。任務(wù)是串行的羡洛,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)藕漱。

/**

  • 同步執(zhí)行 + 串行隊列

  • 特點:不會開啟新線程欲侮,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的肋联,執(zhí)行完一個任務(wù)威蕉,再執(zhí)行下一個任務(wù)。

*/

  • (void)syncSerial {

    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程

    NSLog(@"syncSerial---begin");

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);



dispatch_sync(queue, ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});

dispatch_sync(queue, ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});

dispatch_sync(queue, ^{

    // 追加任務(wù)3

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



NSLog(@"syncSerial---end");

}


輸出結(jié)果

<pre>

currentThread—-{number = 1, name = main}

syncSerial—-begin

1—-{number = 1, name = main}

1—-{number = 1, name = main}

2—-{number = 1, name = main}

2—-{number = 1, name = main}

3—-{number = 1, name = main}

3—-{number = 1, name = main}

syncSerial—-end

</pre>

結(jié)論:

1. 所有任務(wù)都是在當(dāng)前線程執(zhí)行橄仍,并沒有開啟新的線程(`同步執(zhí)行`不具備開啟新線程的能力)

2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之間執(zhí)行的(`同步線程`需要等待隊列的任務(wù)執(zhí)行結(jié)束)韧涨。

3. 任務(wù)是按順序執(zhí)行的(`串行隊列`每次只有一個任務(wù)被執(zhí)行牍戚,任務(wù)一個接一個按順序執(zhí)行)

>綜上所述,`同步執(zhí)行`沒有開啟新線程能力虑粥,`同步執(zhí)行`需要等待隊列任務(wù)執(zhí)行完才能繼續(xù)執(zhí)行

####4.異步執(zhí)行 + 串行隊列

* 會開啟新線程如孝,但是因為任務(wù)是串行的,執(zhí)行完一個任務(wù)娩贷,再執(zhí)行下一個任務(wù)

/**

  • 異步執(zhí)行 + 串行隊列

  • 特點:會開啟新線程第晰,但是因為任務(wù)是串行的,執(zhí)行完一個任務(wù)彬祖,再執(zhí)行下一個任務(wù)茁瘦。

*/

  • (void)asyncSerial {

    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程

    NSLog(@"asyncSerial---begin");

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);



dispatch_async(queue, ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});

dispatch_async(queue, ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});

dispatch_async(queue, ^{

    // 追加任務(wù)3

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



NSLog(@"asyncSerial---end");

}


輸出結(jié)果

<pre>

currentThread—-{number = 1, name = main}

asyncSerial—-begin

asyncSerial—-end

1—-{number = 3, name = (null)}

1—-{number = 3, name = (null)}

2—-{number = 3, name = (null)}

2—-{number = 3, name = (null)}

3—-{number = 3, name = (null)}

3—-{number = 3, name = (null)}

</pre>

結(jié)論:

1. 開啟了一條新線程(`異步線程`具備開啟新線程的能力,`串行隊列`只開啟一個線程)

2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之后才執(zhí)行的(`異步執(zhí)行`不會做任何等待储笑,可以繼續(xù)執(zhí)行任務(wù))

3. 任務(wù)是按順序執(zhí)行的(`串行隊列`每次只有一個任務(wù)被執(zhí)行甜熔,任務(wù)一個接一個按順序執(zhí)行)

>綜上所述,`異步執(zhí)行`具備開啟新線程能力突倍,但不會等待腔稀,`串行隊列`只開啟一條新線程執(zhí)行任務(wù)

####5.同步執(zhí)行 + 主隊列

**主隊列:**

* GCD自帶的一種特殊的串行隊列

* 所有放在主隊列中的任務(wù),都會放在主線程中執(zhí)行

* 使用 `dispatch_get_main_queue()`獲得主隊列

####5.1在主線程中調(diào)用 <font size = 3 /font>`同步執(zhí)行 + 主隊列`</font>

* 互相等待卡死

/**

  • 同步執(zhí)行 + 主隊列

  • 特點(主線程調(diào)用):互等卡主不執(zhí)行羽历。

  • 特點(其他線程調(diào)用):不會開啟新線程焊虏,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)窄陡。

*/

  • (void)syncMain {
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程

NSLog(@"syncMain---begin");



dispatch_queue_t queue = dispatch_get_main_queue();



dispatch_sync(queue, ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_sync(queue, ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_sync(queue, ^{

    // 追加任務(wù)3

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



NSLog(@"syncMain---end");

}


輸出結(jié)果

<pre>

currentThread—-{number = 1, name = main}

syncMain—-begin

(lldb)

</pre>

結(jié)論:

1. 在主線程中使用`同步執(zhí)行 + 主隊列`炕淮,追加到主線程的任務(wù)1拆火,2跳夭,3都不再執(zhí)行了,而且`syncMain---end`也沒有打印们镜,還報崩潰

**原因:**這是因為我們在主線程中執(zhí)行`syncMain`方法币叹,相當(dāng)于把`syncMain`任務(wù)放到了主線程的隊列中。而`同步線程`會等待當(dāng)前隊列中的任務(wù)執(zhí)行完畢模狭,才會接著執(zhí)行颈抚。那么當(dāng)我們把`任務(wù)1`追加到主隊列中,`任務(wù)1`就在等待主線程處理完`sysncMain`任務(wù)嚼鹉,而`syncMain`任務(wù)需要等待`任務(wù)1`執(zhí)行完畢才能接著執(zhí)行贩汉。那么,現(xiàn)在的情況就是`syncMain`任務(wù)和`任務(wù)1`都在等對方執(zhí)行完畢锚赤,相互等待匹舞,所以就卡死了

####5.2 在其他線程中調(diào)用 <font size = 3 /font>`同步執(zhí)行 + 主隊列`<\font>

* 不會開啟新線程,執(zhí)行完一個任務(wù)线脚,再執(zhí)行下一個任務(wù)

// 使用 NSThread 的 detachNewThreadSelector 方法會創(chuàng)建線程赐稽,并自動啟動線程執(zhí)行

selector 任務(wù)

[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];


輸出結(jié)果

<pre>

currentThread—-{number = 3, name = (null)}

syncMain—-begin

1—-{number = 1, name = main}

1—-{number = 1, name = main}

2—-{number = 1, name = main}

2—-{number = 1, name = main}

3—-{number = 1, name = main}

3—-{number = 1, name = main}

syncMain—-end

</pre>

結(jié)論:

1. 所有任務(wù)都是在主線程(非當(dāng)前線程)中執(zhí)行叫榕,沒有開啟新線程(所有在`主隊列`的任務(wù)都會在`主線程`執(zhí)行)

2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之間執(zhí)行(`同步任務(wù)`需要等待隊列的任務(wù)執(zhí)行結(jié)束)

3. 任務(wù)是按順序執(zhí)行的(`主隊列`是`串行隊列`,每次只有一個任務(wù)執(zhí)行姊舵,任務(wù)一個接一個執(zhí)行)

為何不會卡孜铩?

因為`sysncMain`任務(wù)放在其他線程里括丁,而且任務(wù)1荞下,2,3都在追加到主隊列中躏将,這三個任務(wù)會在主線程中執(zhí)行锄弱。`syncMain`任務(wù)在其他線程中執(zhí)行到追加 任務(wù)1 到主隊列中,因為主隊列現(xiàn)在沒有正在執(zhí)行的任務(wù)祸憋,所以會直接執(zhí)行主隊列的 任務(wù)1会宪,之后 2,3 不會卡死

###6 異步執(zhí)行 + 主隊列

* 只在主線程中執(zhí)行任務(wù)蚯窥,執(zhí)行完一個再執(zhí)行下一個

/**

  • 異步執(zhí)行 + 主隊列

  • 特點:只在主線程中執(zhí)行任務(wù)掸鹅,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)

*/

  • (void)asyncMain {

    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程

    NSLog(@"asyncMain---begin");

dispatch_queue_t queue = dispatch_get_main_queue();



dispatch_async(queue, ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_async(queue, ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_async(queue, ^{

    // 追加任務(wù)3

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



NSLog(@"asyncMain---end");

}


輸出結(jié)果

<pre>

currentThread—-{number = 1, name = main}

asyncMain—-begin

asyncMain—-end

1—-{number = 1, name = main}

1—-{number = 1, name = main}

2—-{number = 1, name = main}

2—-{number = 1, name = main}

3—-{number = 1, name = main}

3—-{number = 1, name = main}

</pre>

結(jié)論

1. 所有任務(wù)都在當(dāng)前線程(主線程)執(zhí)行拦赠,并沒有開啟新的線程(雖然`異步執(zhí)行`具備開啟線程的能力巍沙,但因為是主隊列,所以所有任務(wù)都是在主線程中)

2. 所有任務(wù)都在`syncConcurrent—-begin`和`syncConcurrent—-end`之后執(zhí)行(異步執(zhí)行不會做任何等待荷鼠,可以繼續(xù)執(zhí)行任務(wù))

3. 任務(wù)是按順序執(zhí)行的(因為主隊列是`串行隊列`句携,每次只能執(zhí)行一個任務(wù))

###線程通訊

通過 `dispatch_async`、`dispatch_sync`進行通訊

###GCD的其他方法

####1.GCD 柵欄方法:dispatch_barrier_async

* 有時我們需要異步執(zhí)行兩組操作允乐,而且第一組操作執(zhí)行完之后矮嫉,才能開始執(zhí)行第二組操作牍疏。這樣我們就需要一個相當(dāng)于 `柵欄` 一樣的一個方法將兩組異步執(zhí)行的操作組給分割起來蠢笋,當(dāng)然的操作組里面可以包含一個或多個任務(wù),這就需要用到`dispatch_barrier_async`方法在兩個操作組間形成柵欄鳞陨。

<p>`dispatch_barrier_async`函數(shù)會等待前邊追加到并發(fā)隊列中的任務(wù)全部執(zhí)行完畢之后昨寞,再將指定的任務(wù)追加到該異步隊列中。然后在`dispatch_barrier_async`函數(shù)追加的任務(wù)執(zhí)行完畢之后厦滤,異步隊列才恢復(fù)為一般動作服赎,接著追加任務(wù)到該異步隊列并開始執(zhí)行讼撒。如下圖![圖](http://upload-images.jianshu.io/upload_images/10443211-27884f07b52dfa89.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

代碼如下

/**

  • 柵欄方法 dispatch_barrier_async

*/

  • (void)barrier {

    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});

dispatch_async(queue, ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_barrier_async(queue, ^{

    // 追加任務(wù) barrier

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"barrier---%@",[NSThread currentThread]);// 打印當(dāng)前線程

    }

});



dispatch_async(queue, ^{

    // 追加任務(wù)3

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});

dispatch_async(queue, ^{

    // 追加任務(wù)4

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"4---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});

}


輸出結(jié)果:

<pre>

1—-{number = 4, name = (null)}

2—-{number = 3, name = (null)}

1—-{number = 4, name = (null)}

2—-{number = 3, name = (null)}

barrier—-{number = 4, name = (null)}

barrier—-{number = 4, name = (null)}

4—-{number = 3, name = (null)}

3—-{number = 4, name = (null)}

4—-{number = 3, name = (null)}

3—-{number = 4, name = (null)}

</pre>

結(jié)論:

1. 在執(zhí)行完柵欄前面的操作之后,才執(zhí)行柵欄操作,最后再執(zhí)行后邊的操作

####2.GCD 延時方法:dispatch_after

`dispatch_after`:在指定時間之后執(zhí)行某個任務(wù)命爬。

需要注意的是:`dispatch_after`函數(shù)并不是在指定時間之后才開始執(zhí)行處理笔宿,而是在指定時間之后**<a>將任務(wù)追加到主隊列</a>**中律姨。嚴(yán)格來說,這個時間并不絕對準(zhǔn)確限寞。

####3.GCD 一次性代碼(只執(zhí)行一次):dispatch_once

* 我們在創(chuàng)建單例、或者有整個程度運行過程中只執(zhí)行一次的代碼時使用

* `dispatch_once`函數(shù)能保證代碼只執(zhí)行一次仰坦,并且即使在多線程的環(huán)境下也能保證線程安全

/**

  • 一次性代碼(只執(zhí)行一次)dispatch_once

*/

  • (void)once {

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

      // 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
    

    });

}


####4.GCD 快速迭代方法:dispatch_apply

* 通常我們會用 for循環(huán) 遍歷履植,但是GCD給我們提供了快速迭代的函數(shù)`dispatch_apply`。`dipatch_apply`按照指定的次數(shù)將指定的任務(wù)追加到指定的隊列中悄晃,并等待全部隊列執(zhí)行結(jié)束

如果是在串行隊列中使用`dipatch_apply`玫霎,那么就跟 for 循環(huán)一樣,按順序同步執(zhí)行妈橄∈可這樣就體現(xiàn)不出快速迭代的意義了。

<p>我們可以利用并發(fā)隊列進行異步執(zhí)行眷蚓。比如說遍歷0-5這6個數(shù)字鼻种,for循環(huán)的做法是每次取出一個元素,逐個遍歷沙热。`dispatch_apply`可以在多個線程中同時(異步)遍歷多個數(shù)字叉钥。

<p>還有一點,無論是在串行隊列篙贸,還是異步隊列中投队,dipatch_apply 都會等待全部任務(wù)執(zhí)行完畢,這點就像是同步操作爵川,也像是隊列組中的`dispatch_group_wait`方法

/**

  • 快速迭代方法 dispatch_apply

*/

  • (void)apply {

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

NSLog(@"apply---begin");

dispatch_apply(6, queue, ^(size_t index) {

    NSLog(@"%zd---%@",index, [NSThread currentThread]);

});

NSLog(@"apply---end");

}


輸出結(jié)果

<pre>

apply—-begin

1—-{number = 3, name = (null)}

0—-{number = 1, name = main}

2—-{number = 4, name = (null)}

3—-{number = 5, name = (null)}

4—-{number = 3, name = (null)}

5—-{number = 1, name = main}

apply—-end

</pre>

結(jié)論

因為是在并發(fā)隊列中異步執(zhí)行任務(wù)敷鸦,所以各個任務(wù)的執(zhí)行時間 長短不定,最后順序也不定寝贡,但是`apply_end`一定在最后執(zhí)行扒披。這是因為`dispatch_apply`函數(shù)會等待全部任務(wù)執(zhí)行完畢。

>綜上所述兔甘,`dispatch_apply`適用于谎碍,當(dāng)前需要等待某堆任務(wù)(可以是并發(fā)任務(wù)隊列)完成后才能繼續(xù)操作的情況

####5.GCD 的隊列組:dispatch_group

有時候我們會有這樣的需求:分別異步執(zhí)行2個耗時任務(wù)鳞滨,然后當(dāng)2個任務(wù)都執(zhí)行完再回到主線程洞焙,這時就要用到 GCD 的隊列組。

* 要調(diào)用隊列組的`dispatch_group_async`先把任務(wù)放到隊列中拯啦,然后將隊列放入隊列組中澡匪。或者使用隊列組的 `dispatch_group_enter`褒链、`dispatch_group_leave`組合 來實現(xiàn)`dispatch_group_async`.

* 調(diào)用隊列組的`dispatch_group_notify`回到指定線程執(zhí)行任務(wù)唁情。或者使用`dispatch_group_wait`回到當(dāng)前線程繼續(xù)向下執(zhí)行(會阻塞當(dāng)前線程)

#####5.1 dipatch\_group\_notify

* 監(jiān)聽 group 中任務(wù)的完成狀態(tài)甫匹,當(dāng)所有的任務(wù)都執(zhí)行完成后甸鸟,追加任務(wù)到group中惦费,并執(zhí)行任務(wù)

/**

  • 隊列組 dispatch_group_notify

*/

  • (void)groupNotify {

    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程

    NSLog(@"group---begin");

dispatch_group_t group =  dispatch_group_create();



dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    // 等前面的異步任務(wù)1、任務(wù)2都執(zhí)行完畢后抢韭,回到主線程執(zhí)行下邊任務(wù)

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

    NSLog(@"group---end");

});

}


輸出結(jié)果:

<pre>

currentThread—-{number = 1, name = main}

group—-begin

1—-{number = 4, name = (null)}

2—-{number = 3, name = (null)}

2—-{number = 3, name = (null)}

1—-{number = 4, name = (null)}

3—-{number = 1, name = main}

3—-{number = 1, name = main}

group—-end

</pre>

結(jié)論:

當(dāng)所有任務(wù)都執(zhí)行完成之后才執(zhí)行`dispatch_group_notify`block中的任務(wù)

####5.2 dispatch\_group\_wait

* 暫停當(dāng)前線程(阻塞當(dāng)前線程)薪贫,等待指定的group 中的任務(wù)執(zhí)行完成后,才會繼續(xù)執(zhí)行

/**

  • 隊列組 dispatch_group_wait

*/

  • (void)groupWait {

    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程

    NSLog(@"group---begin");

dispatch_group_t group =  dispatch_group_create();



dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

});



// 等待上面的任務(wù)全部完成后刻恭,會往下繼續(xù)執(zhí)行(會阻塞當(dāng)前線程)

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);



NSLog(@"group---end");

}


輸出結(jié)果:

<pre>

currentThread—-{number = 1, name = main}

group—-begin

2—-{number = 4, name = (null)}

1—-{number = 3, name = (null)}

2—-{number = 4, name = (null)}

1—-{number = 3, name = (null)}

group—-end

</pre>

結(jié)論:

dispatch\_group\_wait會等待所有g(shù)roup任務(wù)完成后才繼續(xù)執(zhí)行

####5.3 dispatch\_group\_enter瞧省、dispatch\_group\_leave

* `dispatch_group_enter`標(biāo)志著一個任務(wù)追加到group,執(zhí)行一次鳍贾,相當(dāng)于group中未執(zhí)行完畢任務(wù)數(shù)+1

* `dispatch_group_leave`標(biāo)志著一個任務(wù)離開group鞍匾,執(zhí)行一次,相當(dāng)于group中未執(zhí)行完畢任務(wù)數(shù)-1

* 當(dāng) group 中未執(zhí)行完畢任務(wù)數(shù)為0時骑科,才會拿`dispatch_group_wait`解除阻塞橡淑,以及執(zhí)行追加到`dispatch_group_notify`中的任務(wù)

/**

  • 隊列組 dispatch_group_enter、dispatch_group_leave

*/

  • (void)groupEnterAndLeave

{

NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程

NSLog(@"group---begin");



dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_enter(group);

dispatch_async(queue, ^{

    // 追加任務(wù)1

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

    dispatch_group_leave(group);

});



dispatch_group_enter(group);

dispatch_async(queue, ^{

    // 追加任務(wù)2

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

    dispatch_group_leave(group);

});



dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    // 等前面的異步操作都執(zhí)行完畢后咆爽,回到主線程.

    for (int i = 0; i < 2; ++i) {

        [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程

    }

    NSLog(@"group---end");

});

// // 等待上面的任務(wù)全部完成后梳码,會往下繼續(xù)執(zhí)行(會阻塞當(dāng)前線程)

// dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

//

// NSLog(@"group---end");

}


輸出結(jié)果

<pre>

currentThread—-{number = 1, name = main}

group—-begin

1—-{number = 4, name = (null)}

2—-{number = 3, name = (null)}

1—-{number = 4, name = (null)}

2—-{number = 3, name = (null)}

3—-{number = 1, name = main}

3—-{number = 1, name = main}

group—-end

</pre>

結(jié)論

當(dāng)所有任務(wù)執(zhí)行完成之后,才執(zhí)行 `dispatch_group_notify`中的任務(wù)伍掀,這里 `dispatch_group_enter`掰茶、`dispatch_group_leave`組合,其實等同于`dispatch_group_async`.

####6.GCD 信號量:dispatch_semaphore

GCD 中的信號量是指 Dispatch Semaphore,是持有計數(shù)的信號蜜笤。類似于高速路收費站的欄桿濒蒋。可以通過時把兔,打開欄桿沪伙,不可以通過時,關(guān)閉欄桿县好。在Dispatch_Semaphore 中围橡,使用計數(shù)來完成這個功能,計數(shù)為0時等待缕贡,不可通過翁授。計數(shù)為1或者大于1時,計數(shù)減1且不等待晾咪,可通過收擦。

Dispatch Semaphore 提供了三個函數(shù)

  * `dispatch_semaphore_create`:創(chuàng)建一個Semaphore并初始化信號的總量

  * `dispatch_semaphore_signal`:發(fā)送一個信號,讓信號問題加1

  * `dispatch_semaphore_wait`:可以使用總信號量減1谍倦,當(dāng)信號總量為0時就會一直等待(阻塞所有線程)塞赂,否則就可以正常執(zhí)行

>注意:信號量的使用前提是:想清楚你需要處理哪個線程等待(阻塞),又要哪個線程繼續(xù)執(zhí)行昼蛀,然后使用信號量

Dispatch Semaphore 在實際開發(fā)中主要用于:

1. 保持線程同步宴猾,將異步執(zhí)行任務(wù)轉(zhuǎn)換成同步執(zhí)行任務(wù)

2. 保證線程安全圆存,為線程加鎖

####6.1 Dispatch Semaphore 線程同步

當(dāng)異步執(zhí)行耗時任務(wù),并使用異步執(zhí)行的結(jié)果進行一些額外的操作時仇哆,我們要用到這個辽剧。換句話說,相當(dāng)于税产,將異步執(zhí)行任務(wù)轉(zhuǎn)換成同步執(zhí)行任務(wù).比如:AFNetworking中AFURLSessionManager.m 里面的 `tasksForKeyPath:`方法怕轿。通過引入信號量的方式,等待異步執(zhí)行任務(wù)結(jié)果辟拷,獲取到tasks撞羽,然后再返回該tasks。

  • (NSArray *)tasksForKeyPath:(NSString *)keyPath {

    __block NSArray *tasks = nil;

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {

      if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
    
          tasks = dataTasks;
    
      } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
    
          tasks = uploadTasks;
    
      } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
    
          tasks = downloadTasks;
    
      } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
    
          tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
    
      }
    
      dispatch_semaphore_signal(semaphore);
    

    }];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return tasks;

}


下面衫冻,我們來利用 Dispatch Semaphore 實現(xiàn)線程同步诀紊,將異步任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)

/**

  • semaphore 線程同步

*/

  • (void)semaphoreSync {
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程

NSLog(@"semaphore---begin");



dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);



__block int number = 0;

dispatch_async(queue, ^{

    // 追加任務(wù)1

    [NSThread sleepForTimeInterval:2];              // 模擬耗時操作

    NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程



    number = 100;



    dispatch_semaphore_signal(semaphore);

});



dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

NSLog(@"semaphore---end,number = %zd",number);

}


輸出結(jié)果:

<pre>

currentThread—-{number = 1, name = main}

semaphore—-begin

1—-{number = 3, name = (null)}

semaphore—-end,number = 100

</pre>

結(jié)論:

* `semaphore---end`是在執(zhí)行完 `number = 100;`之后才打印的。而且輸出結(jié)果 number 為100隅俘;

這是因為`異步執(zhí)行`不會做任務(wù)等待邻奠,可以繼續(xù)執(zhí)行任務(wù),`異步執(zhí)行`將任務(wù)1追加到隊列之后为居,不做等待碌宴,接著執(zhí)行 `dispatch_semaphore_wait`方法。此時 semaphore == 0蒙畴,當(dāng)前線程進入等待狀態(tài)贰镣。然后異步任務(wù)1開始執(zhí)行。任務(wù)1執(zhí)行到`dispatch_semaphore_signal`之后膳凝,總信號量碑隆,此時為1,`dispatch_semaphore_wait`方法使用總信號量減1蹬音,正在被阻塞的線程恢復(fù)繼續(xù)執(zhí)行上煤。

####6.2 Dispatch Semaphore 線程安全和線程同步(為線程加鎖)

最后的學(xué)習(xí)總結(jié):

  1. GCD會自動管理線程生命周期
  2. GCD會充分使用CPU內(nèi)核
  3. GCD中同步線程不能創(chuàng)建新線程,只能使用當(dāng)前線程著淆,故無論是串行還是并發(fā)都是串行執(zhí)行任務(wù)
  4. GCD中異步線程可以創(chuàng)建新線程劫狠,并發(fā)隊列會根據(jù)實際情況創(chuàng)建1條或者多條線程并發(fā)執(zhí)行執(zhí)行,串行隊列只開辟一條新線程并且新線程中串行執(zhí)行任務(wù)
  5. 主線程為特殊同步線程牧抽,主隊列是串行隊列且只能在主線程中串行執(zhí)行嘉熊,不可創(chuàng)建新的主線程遥赚,故在主線程中通過同步添加任務(wù)到主隊列中會形成相互等的死鎖狀態(tài)
  6. dispatch_barrier_async 欄柵扬舒,通過dispatch_barrier_async或隔開隊列執(zhí)行 無論dispatch_barrier_async前是串行還是并發(fā)隊列,在隊列任務(wù)全部執(zhí)行完之前 會一直等待凫佛,完成后執(zhí)行dispatch_barrier_async中的代碼塊讲坎,執(zhí)行完后才會繼續(xù)往下執(zhí)行
  7. dispatch_group_async將任務(wù)添加到隊列后再把隊列添加到group里 當(dāng)group執(zhí)行完會執(zhí)行dispatch_group_notify里面的代碼塊
  8. dispatch_group_async也可以在添加完任務(wù)后dispatch_group_wait(group, DISPATCH_TIME_FOREVER);阻塞線程來達到執(zhí)行完group后才執(zhí)行其他代碼的目的
  9. dispatch_group_async也可以用 dispatch_group_enter(追加group+1)孕惜、dispatch_group_leave(group-1),但一定要配套使用
  10. dispatch_semaphore dispatch_semaphore_create(創(chuàng)建)、dispatch_semaphore_signal(發(fā)送信號晨炕、發(fā)送后dispatch_semaphore_wait會進行判斷是否能解除線程阻塞)衫画、dispatch_semaphore_wait(判斷信號是否為0,為0時線程會一直等待阻塞線程直接信號量變?yōu)?之后)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓮栗,一起剝皮案震驚了整個濱河市削罩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌费奸,老刑警劉巖弥激,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異愿阐,居然都是意外死亡微服,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門缨历,熙熙樓的掌柜王于貴愁眉苦臉地迎上來以蕴,“玉大人,你說我怎么就攤上這事辛孵〈园梗” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵魄缚,是天一觀的道長腾供。 經(jīng)常有香客問我,道長鲜滩,這世上最難降的妖魔是什么伴鳖? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮徙硅,結(jié)果婚禮上榜聂,老公的妹妹穿的比我還像新娘。我一直安慰自己嗓蘑,他們只是感情好须肆,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桩皿,像睡著了一般豌汇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泄隔,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天拒贱,我揣著相機與錄音,去河邊找鬼。 笑死逻澳,一個胖子當(dāng)著我的面吹牛闸天,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斜做,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼苞氮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瓤逼?” 一聲冷哼從身側(cè)響起笼吟,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎霸旗,沒想到半個月后赞厕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡定硝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年皿桑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔬啡。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡诲侮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箱蟆,到底是詐尸還是另有隱情沟绪,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布空猜,位于F島的核電站绽慈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辈毯。R本人自食惡果不足惜坝疼,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谆沃。 院中可真熱鬧钝凶,春花似錦、人聲如沸唁影。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽据沈。三九已至哟沫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锌介,已是汗流浹背嗜诀。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裹虫。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓肿嘲,卻偏偏與公主長得像融击,于是被迫代替她去往敵國和親筑公。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345