GCD

參考文檔1
參考文檔2
參考文檔3


參考文檔4
參考文檔5

Grand Central Dispatch(GCD) 是 Apple 開發的一個多核編程的較新的解決方法。它主要用于優化應用程序以支持多核處理器以及其他對稱多處理系統。

1. GCD術語

1.1串行Serial

任務串行執行時,一次只能執行一個任務。

1.2并行Parallelism

并行指多個任務同時執行
多核設備可以同時執行多個線程;單核設備如果想要并行執行任務,就需要運行一個線程,執行環境切換(也稱上下文切換,context switch),運行另外一個線程或進程(process),這通常很快發生,足以形成并行執行的錯覺

1.3并發

并發指task同時(simultaneously)發生,可以被同時執行,但具體是否同時執行由GCD根據當前負載決定

1.4同步Synchronous

同步執行會等待提交的task執行完畢后,再繼續執行后面任務。我們平常調用的方法都是同步方法,如第一行調用doSomething:方法,程序執行第二行時,doSomething:方法肯定執行完畢了。同步執行一些耗時操作時會堵塞當前線程。調用dispatch_sync函數并將任務提交到當前隊列(即dispatch_sync函數所在隊列)會導致死鎖。
因為是同步調用,目標隊列不會復制塊(Block_copy),而只引用塊。為提高性能,同步函數一般盡可能在當前線程調用塊

1.5異步Asynchronous

異步指調用任務后立即返回,不需要等待任務執行。異步不會堵塞當前線程

1.6死鎖Dead Lock

兩個或多個任務、線程都在等待對方完成操作。第一個任務無法完成,因為它在等待第二個任務完成。第二個任務無法完成,因為它在等待第一個任務完成。兩個任務相互等待,這就形成了死鎖。

1.7線程安全Thread Safe

線程安全的代碼可以在多個線程或并發任務中同時執行。非線程安全的代碼一次只能在一個環境中運行。例如,NSArray是線程安全的,可以同時在多個線程或并發任務中執行。而NSMutableArray不是線程安全的,一次只能在一個環境中運行

1.8環境切換Context Switch

Context switch指當在同一個處理器處理不同線程時,存儲和恢復執行狀態的過程。編寫多任務應用程序時,進行上下文切換非常常見,但會產生額外的開銷。

2. 兩種隊列

GCD提供的調度隊列( dispatch queue)是一個類似于對象的結構,用于管理向其提交的任務。
所有的dispatch queue都是先進先出(first in, first out. 簡稱FIFO)的數據結構,因此,隊列中任務運行順序與添加順序一致,即第一個進入隊列的任務,第一個被執行;第二個進入隊列的任務,第二個被執行;以此類推。

所有dispatch queue自身都是線程安全的
GCD的隊列分為串行隊列(Serial Queues)和并發隊列(Concurrent Queues)兩種

兩種隊列都用dispatch_queue_create創建,傳入兩上參數,
第1個參數是隊列名稱(唯一標識符),debugger和性能工具會顯示此處隊列名稱以幫助跟蹤任務執行情況,可為空。
第2個參數用于標識隊列類型,串行/并發。

//串行隊列的創建
dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);
//并發隊列的創建
dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);


//主隊列(Main Dispatch Queue)
//主隊列是一種特殊的串行隊列
**所有放在主隊列中的任務,都會放到主線程中執行**
dispatch_queue_t mainQueue = dispatch_get_main_queue();

//全局并行隊列(Global Dispatch Queue)
//第1個參數表示隊列優先級,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。
//第2個參數暫時沒用,用0即可。
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);

隊列是指執行任務的等待隊列,即用來存放任務的隊列。
是一種特殊的線性表,采用 FIFO(先進先出)的原則,由GCD內部自行控制。

同步執行和異步執行 指的都是對一個已經創建的隊列,把任務(代碼/代碼塊)添加進去的方式。

同步執行和異步執行區別:

  1. 會不會阻塞當前線程,是否等待隊列的任務執行結束
  2. 是否具備開啟新線程的能力。

**

  • 串行隊列(Serial Dispatch Queue)
    每次只執行一個任務,讓任務一個接著一個地執行。一個任務執行完畢后,再執行下一個任務。
    只開啟一個新線程(主線程則不開啟新線程,在當前線程執行任務)

  • 并發隊列(Concurrent Dispatch Queue)
    可以讓多個任務并發(同時)執行。
    可以開啟多個線程,并且同時執行任務。

  • 同步執行(sync)
    同步添加任務到指定的隊列中,在添加的任務執行結束之前,會一直等待,直到隊列里面的任務完成之后再繼續執行。
    只能在當前線程中執行任務,不具備開啟新線程的能力。

  • 異步執行(async)
    異步添加任務到指定的隊列中,它不會做任何等待,
    可以繼續執行任務。
    可以在新的線程中執行任務,具備開啟新線程的能力。

2. 任務的創建

GCD 提供了同步執行任務的創建方法dispatch_sync和異步執行任務創建方法dispatch_async。

// 同步執行任務創建方法
dispatch_sync(queue, ^{
    // 這里放同步執行任務代碼
});
// 異步執行任務創建方法
dispatch_async(queue, ^{
    // 這里放異步執行任務代碼
});
1. 異步執行 + 并行隊列
/**
 * 異步執行 + 并行隊列
 * 特點:可以開啟多個線程,任務交替(同時)執行。
 */
- (void)asyncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程
    NSLog(@"asyncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        // 追加任務1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任務2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任務3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    NSLog(@"asyncConcurrent---end");
}

結果 :
所有任務是在打印的
syncConcurrent---begin和syncConcurrent---end之后才執行的。
說明當前線程沒有等待,而是直接開啟了新線程,在新線程中執行任務

總結:
異步執行具備開啟新線程的能力,所以除了當前主線程,系統又開啟了其它線程,任務之間不需要排隊,且是交替/同時執行的。

2. 異步執行 + 串行隊列
/**
 * 異步執行 + 串行隊列
 * 特點:會開啟新線程,但是因為任務是串行的,執行完一個任務,再執行下一個任務。
 */
- (void)asyncSerial {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程
    NSLog(@"asyncSerial---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        // 追加任務1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    dispatch_async(queue, ^{
        // 追加任務2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    dispatch_async(queue, ^{
        // 追加任務3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    NSLog(@"asyncSerial---end");
}

結果
所有任務是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執行的(異步執行不會做任何等待,可以繼續執行任務)。

總結
異步執行具備開啟新線程的能力,但由于是串行隊列所以只開啟一個線程。任務是按順序執行的(串行隊列每次只有一個任務被執行,任務一個接一個按順序執行)

3. 同步執行 + 并行隊列
/**
 * 同步執行 + 并行隊列
 * 特點:在當前線程中執行任務,不會開啟新線程,執行完一個任務,再執行下一個任務。
 **/
- (void)syncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程
    NSLog(@"syncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        // 追加任務1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任務2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任務3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    NSLog(@"syncConcurrent---end");
}

結果 :
所有任務都在打印的syncConcurrent---begin
和syncConcurrent---end之間執行的(同步任務需要等待隊列的任務執行結束),所有 任務按順序執行的

總結:
所有任務都是在當前主線程中執行的,沒有開啟新的線程(同步執行不具備開啟新線程的能力)。
按順序執行的原因:雖然并發隊列可以開啟多個線程,并且同時執行多個任務。但是因為隊列本身不能創建新線程,只有當前線程這一個線程(同步任務不具備開啟新線程的能力),所以也就不存在并發。而且當前線程只有等待當前隊列中正在執行的任務執行完畢之后,才能繼續接著執行下面的操作(同步任務需要等待隊列的任務執行結束)。所以任務只能一個接一個按順序執行,不能同時被執行。

可見并發隊列的并發功能只有在異步(dispatch_async)函數下才有效。

4. 同步執行 + 串行隊列
/**
 * 同步執行 + 串行隊列
 * 特點:不會開啟新線程,在當前線程執行任務。
   任務是串行的,執行完一個任務,再執行下一個任務。
 */
- (void)syncSerial {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程
    NSLog(@"syncSerial---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        // 追加任務1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任務2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任務3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    NSLog(@"syncSerial---end");
}

結果
所有任務都在打印的syncConcurrent---begin和syncConcurrent---end之間執行(同步任務需要等待隊列的任務執行結束)

總結:
這里的執行原理和步驟圖跟“同步執行+并發隊列”是一樣的,只要是同步執行就沒法開啟新的線程,所以多個任務之間也一樣只能按順序來執行
任務是按順序執行的(串行隊列每次只有一個任務被執行,任務一個接一個按順序執行)。

5. 同步執行+主隊列
/**
 * 同步執行 + 主隊列
 * 特點(主線程調用):互等卡主不執行。
 * 特點(其他線程調用):不會開啟新線程,執行完一個任務,再執行下一個任務。
 */
- (void)syncMain {
    
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程
    NSLog(@"syncMain---begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        // 追加任務1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任務2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任務3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    NSLog(@"syncMain---end");
}

總結:
主線程中同步執行+主隊列
在主線程中執行syncMain方法,相當于把syncMain任務放到了主線程的隊列中。而同步執行會等待當前隊列中的任務執行完畢,才會接著執行。那么當我們把任務1追加到主隊列中,任務1就在等待主線程處理完syncMain任務。而syncMain任務需要等待任務1執行完畢,才能接著執行。
現在的情況就是syncMain任務和任務1都在等對方執行完畢。這樣大家互相等待,所以就卡住了,所以我們的任務執行不了,而且syncMain---end也沒有打印。

6.異步執行 + 主隊列
/**
 * 異步執行 + 主隊列
 * 特點:只在主線程中執行任務,執行完一個任務,再執行下一個任務
 */
- (void)asyncMain {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程
    NSLog(@"asyncMain---begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        // 追加任務1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任務2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任務3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當前線程
        }
    });
    
    NSLog(@"asyncMain---end");
}

結果
所有任務是在打印的syncConcurrent—begin和syncConcurrent—end之后才開始執行的(異步執行不會做任何等待,可以繼續執行任務)
總結:
所有任務都是在當前線程(主線程)中執行的,并沒有開啟新的線程(雖然異步執行具備開啟線程的能力,但因為是主隊列,所以所有任務都在主線程中)。
任務是按順序執行的(因為主隊列是串行隊列,每次只有一個任務被執行,任務一個接一個按順序執行)。


任務依賴

一般情況下,異步添加到并行隊列中的任務,我們是沒法控制執行順序的,或者說也是按FIFO順序執行,但執行完成的順序確無法控制。下面是二般情況:

1.Barrier


barrier只能在自己創建的串行/并行隊列中才有效

dispatch_queue_t globalQueue = dispatch_queue_create("ConcurrentQ", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(globalQueue, ^{
        NSLog(@"barrier之前的任務1");
        
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"barrier之前的任務2");
    });
    /***************************/
    //使用dispatch_barrier將任務添加到隊列后,
    //任務會在前面任務全部執行完后執行
    //且任務在執行過程中,其它任務無法執行    
    dispatch_barrier_async(globalQueue, ^{
        
        NSLog(@"barrier ConQ 1");
    });
    /***************************/
    dispatch_async(globalQueue, ^{
        NSLog(@"barrier之后的任務1");
        
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"barrier之后的任務2");
    });
    /***************************/
    
    dispatch_barrier_async(globalQueue, ^{
        
        NSLog(@"barrier ConQ 2");
    });
    
    dispatch_barrier_async(globalQueue, ^{
        NSLog(@"barrier ConQ 3");
    });
   
  //由于是異步添加任務,所以不用等任務執行完,
  //有可能會被首先執行打印 
    NSLog(@"當前隊列的最后一個任務");

2.GCD group

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

 ---------------dipatch_group------------
//barrier將任務添加到一個隊列中,可以同或異步添加
//group將傷添加到一個組當中,再將組放入隊列,異步添加

    dispatch_group_async(group, globalQueue, ^{
       
        NSLog(@"globalQueue1");
    });

    dispatch_group_async(group, globalQueue, ^{
    // sleep是全局休眠定時器
    // group_wait只阻塞當前group block,所以3會先執行
        dispatch_group_wait(gp, dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC));
        NSLog(@"globalQueue2");
    });

    dispatch_group_async(group, globalQueue, ^{
     
        NSLog(@"globalQueue3");
    });

    /*************當group所有任務完成后,會發送完成通知******************/
    dispatch_group_notify(group, globalQueue, ^{
        NSLog(@"1.2.3 已完成");
    });

    //由于是異步添加到并行隊列,這個任務有可能被最先執行完
    dispatch_async(globalQueue, ^{
        
        NSLog(@"globalQueue4");
    });

或者

項目需求是AB進行網絡請求,數據返回后,回主線程合并,但對網絡請求而言,請求發出即算執行完畢。所以上面的寫法就不更適合。

 dispatch_group_t group = dispatch_group_create();

---------------dipatch_group------------

    dispatch_group_enter(group);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"任務一完成");
        dispatch_group_leave(group);
    });

    dispatch_group_enter(group);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"任務二完成");
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任務完成");
    });

網絡請求開始前dispatch_group_enter(group) ,請求成功或失敗后都執行dispatch_group_leave(group)。當所有的enter都leave后會觸發通知。

3.Semaphore

信號量大于0時執行后面代碼,等于0時等待
dispatch_semaphore_create 創建一個semaphore 
dispatch_semaphore_signal 發送一個信號,讓信號總量+1
dispatch_semaphore_wait 信號量為0時,等待信號;信號量大于0時,讓信號量-1,并執行后面代碼。

//創建信號量
dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
//添加任務1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
        
        NSLog(@"第一個任務");
        dispatch_semaphore_signal(semaphore1);//信號量+1
    });


//添加任務2    
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //判斷信號量
        //如果為0,則根據傳入的時間等待
        //如果不為0,則-1執行
        dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
      
        NSLog(@"第二個任務");
        dispatch_semaphore_signal(semaphore2);//信號量+1
    });

///判斷信號量  
dispatch_async(dispatch_get_global_queue(0, 0), ^{
       dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
        NSLog(@"這是最后一個任務");
    });

在網絡請求中所有請求加異步加入到并行隊列后,成功/失敗都要semaphore_signal,然后在異步并行隊列中判斷是否可以執行

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

dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);

dispatch_async(queue,^{
    //第一個網絡請求
    ...  ...
    dispatch_semaphore_signal(semaphore1);
}
dispatch_async(queue,^{
    //第二個網絡請求
      ... ...
    dispatch_semaphore_signal(semaphore2);
}
//semphore_wait并發隊列中執行
dispatch_async(queue,^{
     dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
     dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);

     dispatch_async(dispatch_get_main_queue(), ^{
      //刷新UI   
     });
 });

PS:

dispatch_after是延遲提交,不是延遲運行,所以在串行隊列中,先提交先運行。

//串行隊列
 dispatch_queue_t queue = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);

    NSLog(@"Begin add block...");
    //提交一個block
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:10];
        NSLog(@"First block done...");
    });
    
    //5 秒以后提交block
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
    //dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
    //dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);同樣表示1秒
        NSLog(@"After...");
    });

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容

  • 文用來介紹 iOS 多線程中 GCD 的相關知識以及使用方法。通過本文,您將了解到: 1. GCD 簡介 2. G...
    曉_我想去環游世界閱讀 1,156評論 2 8
  • 文化普及第一篇 霸者,伯也。這是霸字的一種釋義,伯,也就是兄長。春秋戰國時期,諸侯爭霸,意思就是說,各路諸侯爭著當...
    bella火火火火閱讀 255評論 0 0
  • 每一對初婚的戀人,當初都彼此赤誠以待,不在乎對方缺點,接受對方所有一切,禍福與共,風雨同舟。 婚外情中的人們不能做...
    運安閣主閱讀 503評論 0 0
  • 在辦公室拖地時,我感到很喜悅,因為想到大家踏進我清新爽潔的工作區域會產生無比的愉悅感,而且當自己身處其中,也會有很...
    靜宇藍歆閱讀 207評論 0 1
  • 不得不說,我經常忘記自己的年齡,也有人時常夸獎我并不像所謂的80后,但是我知道,真正與年齡不符的,是心智! 嫁給先...
    罌粟花兒開閱讀 175評論 0 0