GCD使用·記錄

一、開端

隊列與任務(wù)創(chuàng)建

  • dispatch_queue_t 自定義GCD隊列,區(qū)分串行隊列與并行隊列
  • dispatch_async(queue, block) 執(zhí)行異步任務(wù)
  • dispatch_sync(queue, block) 執(zhí)行同步任務(wù)

GCD常用方法

  • dispatch_barrier_async(queue, block) 分割執(zhí)行異步任務(wù)塊
  • dispatch_group_t 隊列組淤井,分組執(zhí)行異步/同步任務(wù)
  • dispatch_semaphore_t 信號量吴裤,通常用來保證線程安全哨查,或保持線程同步

GCD其他方法

  • dispatch_after(dispatch_time_t, queue, block) 指定時間之后執(zhí)行隊列中的任務(wù)
  • dispatch_once(&dispatch_once_t, block) 保證任務(wù)只被執(zhí)行一次究驴,同時也能保證線程安全
  • dispatch_apply(count, queue, block) 快速迭代隊列任務(wù),不論并行/串行隊列阻肩,都是逐個遍歷任務(wù)來操作带欢,類似同步操作

二、詳述

前面對于常用的GCD方法做了一個簡要的展示烤惊,對于詳細的使用情況乔煞,這里一一來展開說明。

概念

先說說基本的任務(wù)和隊列:
任務(wù) 就是最基本的執(zhí)行單元柒室,在線程和隊列中渡贾,任務(wù)執(zhí)行被分為異步執(zhí)行同步執(zhí)行

  • 同步執(zhí)行:
    任務(wù)被添加到指定隊列后雄右,按順序執(zhí)行完當前任務(wù)后才會繼續(xù)執(zhí)行其他任務(wù)空骚,在此之前會等待任務(wù)執(zhí)行結(jié)束。此外擂仍,只能在當前線程中執(zhí)行任務(wù)囤屹,不具備開啟新線程能力。

  • 異步任務(wù):
    任務(wù)被添加到指定隊列后逢渔,不會立即處理該任務(wù)肋坚,不做等待,繼續(xù)執(zhí)行后續(xù)任務(wù)肃廓。此外智厌,可以在新線程中執(zhí)行任務(wù),具備開啟線程的能力盲赊。

隊列 相當于一個容器铣鹏,用來存放和調(diào)度任務(wù)的,任務(wù)的同步角钩、異步執(zhí)行都是需要基于其所在的隊列屬性吝沫,隊列的不同,任務(wù)所具備開啟線程的能力也就不同递礼;隊列分為串行隊列并行隊列惨险。

  • 串行隊列:
    每次只有一個任務(wù)被執(zhí)行。所有在此隊列中的任務(wù)脊髓,都是一個接一個的執(zhí)行(基于同步辫愉、異步執(zhí)行規(guī)則)。此外将硝,在此隊列中只會開啟一個線程來執(zhí)行其所有的任務(wù)恭朗。

  • 并行隊列:
    同時可以執(zhí)行多個任務(wù),執(zhí)行順序由隊列(系統(tǒng))調(diào)度(基于同步依疼、異步執(zhí)行規(guī)則)痰腮。此外,在此隊列中可以同時開啟多個線程律罢,同時處理多個任務(wù)膀值,線程數(shù)量的上限基于系統(tǒng)限制。

1. 入口

  • 隊列的建立

    //并行隊列
    dispatch_queue_t queue =   dispatch_queue_create("queue.concurrent",DISPATCH_QUEUE_CONCURRENT);
    //串行隊列
    dispatch_queue_t queue = dispatch_queue_create("queue.serial", DISPATCH_QUEUE_SERIAL);
    

    上面是最直接的隊列創(chuàng)建方法误辑,在GCD中有另外2種特殊的隊列沧踏,不能手動創(chuàng)建,只能直接獲冉矶ぁ:

    主隊列:

    dispatch_get_main_queue();
    

    全局隊列:

     dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
  • 創(chuàng)建任務(wù)

    1. 同步任務(wù)
    //queue為并行/串行隊列翘狱,創(chuàng)建/獲取方式參考上面隊列部分
    dispatch_sync(queue, ^{
          NSLog(@"do sync task here");
    });
    
    1. 異步任務(wù)
    //queue為并行/串行隊列,創(chuàng)建/獲取方式參考上面隊列部分
    dispatch_async(queue, ^{
          NSLog(@"do async task here");
    });
    
  • 任務(wù)與隊列關(guān)系

    區(qū)別 并行隊列 串行隊列 主隊列
    同步任務(wù) 不開啟新線程砰苍,串行執(zhí)行任務(wù) 不開啟新線程潦匈,串行執(zhí)行任務(wù) 主線程調(diào)用:觸發(fā)死鎖
    其他線程調(diào)用:與普通串行隊列同步任務(wù)情況相同
    異步任務(wù) 開啟新線程,并行執(zhí)行任務(wù) 只開啟一條新線程赚导,串行執(zhí)行任務(wù) 沒有開啟新線程历等,串行執(zhí)行任務(wù)

代碼此處不再贅述,網(wǎng)上有很多此類說明辟癌,具體可以參考這篇文章寒屯,相當全面:iOS多線程:『GCD』詳盡總結(jié)

2. 擴展

GCD的基本用法之外,還有許多常用的方法黍少,在本文的開頭已經(jīng)列舉出來了寡夹,下面簡述一下,作為記錄參考厂置。


  • dispatch_barrier_async
    GCD柵欄方法菩掏,用于分割上下兩塊任務(wù)操作,每塊都可以包含多個異步任務(wù)操作昵济。被分割的2塊任務(wù)組可以看做為2個同步執(zhí)行的任務(wù)組智绸,只有當?shù)谝粔K任務(wù)全部執(zhí)行完畢后野揪,才會開始第二塊任務(wù)執(zhí)行。此方法用于并行異步任務(wù)處理中瞧栗,同步任務(wù)處理沒有意義斯稳。

    dispatch_queue_t queue = dispatch_queue_create("queue.serial", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
          NSLog(@"task1 --->%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
          NSLog(@"task2 --->%@",[NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
          NSLog(@"barrier --->%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
          NSLog(@"task3 --->%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
          NSLog(@"task4 --->%@",[NSThread currentThread]);
    });
    

    執(zhí)行結(jié)果:

    task2 ---><NSThread: 0x60400007c800>{number = 1, name = main}
    task1 ---><NSThread: 0x600000466700>{number = 3, name = (null)}
    barrier ---><NSThread: 0x604000467880>{number = 4, name = (null)}
    task4 ---><NSThread: 0x60400007c800>{number = 1, name = main}
    task3 ---><NSThread: 0x604000467880>{number = 4, name = (null)}
    

  • dispatch_group
    GCD隊列組,此方法常用于耗時任務(wù)組的等待操作迹恐,有點類似dispatch_barrier_async方法挣惰,等待一大塊的任務(wù)執(zhí)行完畢后才繼續(xù)執(zhí)行后續(xù)隊列任務(wù)。

    • 2種調(diào)用方式:
      1. 通過dispatch_group_async將任務(wù)所在隊列放到隊列組中殴边,接著通過dispatch_group_notify來回到指定的線程執(zhí)行任務(wù)憎茂。
      2. 通過 dispatch_group_enterdispatch_group_leave組合來操作任務(wù)所在隊列進入/離開隊列組锤岸,接著使用 dispatch_group_notify 來回到指定線程執(zhí)行任務(wù)竖幔。

    此處等待組隊列任務(wù)執(zhí)行完成的方法還有一種:dispatch_group_wait,與dispatch_group_notify有區(qū)別是偷。dispatch_group_wait用于阻塞當前線程赏枚,等待指定group中的所有隊列任務(wù)執(zhí)行完成后,才會繼續(xù)執(zhí)行dispatch_group_wait后面的任務(wù)晓猛,其作用與dispatch_group_notify一致饿幅。

  • dispatch_group_async

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
      
    void(^groupBlock)(NSString *taskName) = ^(NSString* taskName)
    {
        for (int i = 0; i<2; i++)
        {
            [NSThread sleepForTimeInterval:2.];
            NSLog(@"%@ --> %@",taskName,[NSThread currentThread]);
        }
    };
      
    dispatch_group_async(group, queue, ^{
        groupBlock(@"task1");
    });
    dispatch_group_async(group, queue, ^{
        groupBlock(@"task2");
    });
      
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        groupBlock(@"end group task");
    });
    
    /* 
    * 此方法效果與dispatch_group_notify一致,用于等待group中隊列任務(wù)執(zhí)行完畢后戒职,繼續(xù)執(zhí)行其后的其他任務(wù)
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    groupBlock(@"end group task");
    */
    
  • dispatch_group_enterdispatch_group_leave

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
      
    void(^groupBlock)(NSString *taskName, BOOL groupTask) = ^(NSString* taskName, BOOL groupTask)
    {
        for (int i = 0; i<2; i++)
        {
            [NSThread sleepForTimeInterval:2.];
            NSLog(@"%@ --> %@",taskName,[NSThread currentThread]);
        }
        if (groupTask)
        {
            //如果為group任務(wù)栗恩,則離開group隊列
            dispatch_group_leave(group);
        }
    };
      
    dispatch_group_enter(group);//進入group隊列
    dispatch_group_async(group, queue, ^{
        groupBlock(@"task1",YES);
    });
      
    dispatch_group_enter(group);//進入group隊列
    dispatch_group_async(group, queue, ^{
        groupBlock(@"task2",YES);
    });
    
    //等待上面的任務(wù)全部完成后,會繼續(xù)往下執(zhí)行洪燥,在此之前磕秤,此處阻塞了線程
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    groupBlock(@"end group task",NO);
    

    以上展示了2種執(zhí)行組隊列任務(wù)的方式,分別用了dispatch_group_notifydispatch_group_wait來等待組隊列執(zhí)行完畢捧韵。


  • dispatch_semaphore_t
    GCD信號量市咆,通過操作信號量的增減,可以達到線程操作安全的目的再来。

    此方式提供了3個函數(shù)方法:

    • dispatch_semaphore_create 創(chuàng)建并初始化信號量
    • dispatch_semaphore_signal 發(fā)送一個信號蒙兰,信號量增加1
    • dispatch_semaphore_wait 減少1個信號量,當信號量小于0時芒篷,將會阻塞所在線程搜变,否則繼續(xù)執(zhí)行(注:為0時依舊繼續(xù)執(zhí)行)

    此方式常用于:

    • 保持線程同步,將異步任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
    • 保證線程安全针炉,為線程加鎖

    線程同步

    __block int num = 0;
      
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_queue_t queue = dispatch_queue_create("queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
          num = 100;
          dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"num = %d",num);
    

    輸出結(jié)果:

    num = 100
    

    可以看出挠他,原本異步執(zhí)行的任務(wù),卻在主線程輸出任務(wù)之前執(zhí)行了篡帕,說明在輸出num之前殖侵,線程處于阻塞狀態(tài)贸呢。

    此處如果將創(chuàng)建時的信號量改為1,則無法達到同步線程目的拢军,異步執(zhí)行的任務(wù)依舊在主線程輸出num值之后執(zhí)行楞陷。

    線程安全

    __block NSInteger saleNum = 0;
    NSInteger maxCount = 100;
      
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_queue_t sale_queue1 = dispatch_queue_create("sale1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t sale_queue2 = dispatch_queue_create("sale2", DISPATCH_QUEUE_CONCURRENT);
      
    void(^saleBlock)(void) = ^()
    {
        while (saleNum<maxCount)
        {
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//加鎖
            if (saleNum<maxCount)
            {
                saleNum++;
                NSLog(@"已售出:%ld -> %@",saleNum,[NSThread currentThread]);
                [NSThread sleepForTimeInterval:0.2];
            }
            else
            {
                NSLog(@"已售完 -> %@",[NSThread currentThread]);
                dispatch_semaphore_signal(semaphore);//解鎖
                break;
            }
              
            dispatch_semaphore_signal(semaphore);//解鎖
        }
    };
      
    //售貨員1
    dispatch_async(sale_queue1, ^{
        saleBlock();
    });
      
    //售貨員2
    dispatch_async(sale_queue2, ^{
        saleBlock();
    });
    

    輸出結(jié)果太長,不在此處展示朴沿,最后得到的輸出順序是按照常規(guī)遞增方式來展現(xiàn)的猜谚,即 1败砂,2赌渣,3,....昌犹,100坚芜,已售完。其只使用了1個信號的增量斜姥,來控制庫存加法的異步任務(wù)鸿竖,在同一時間只能由一個線程執(zhí)行,這樣就保證了該庫存數(shù)據(jù)的準確性铸敏。

    這里的主要思路是:總信號量為1缚忧,在進入執(zhí)行加法任務(wù)前,先通過dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 方法減少一個信號量杈笔,使總信號量為0闪水,保證當前線程無阻塞可以繼續(xù)執(zhí)行,如果在此同時另外一條線程插入進來開始訪問此任務(wù)蒙具,那么信號量將繼續(xù)減少(因為第二條線程也會走一次dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)方法)球榆,變?yōu)樨摂?shù),則該線程阻塞禁筏,這時持钉,只能先等待第一條線程執(zhí)行完此加法任務(wù)后,通過dispatch_semaphore_signal(semaphore)方法增加一個信號量篱昔,解鎖第二條線程阻塞的情況每强,同時第二條線程將繼續(xù)執(zhí)行加法,如此循環(huán)下去州刽。


  • 其他方法
    • dispatch_after
      GCD延時執(zhí)行方法舀射,可以指定多久后執(zhí)行某個任務(wù),執(zhí)行此方法后怀伦,在指定時間之后才會將任務(wù)追加到隊列中脆烟,并不是到指定時間后才開始執(zhí)行任務(wù),所以指定的執(zhí)行時間并不是絕對準確的房待。

      NSLog(@"task0 --> %@", [NSThread currentThread]);
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          NSLog(@"task1 --> %@", [NSThread currentThread]);
      });
      

      打印結(jié)果很明顯能看到在task0之后邢羔,延遲了一段時間才執(zhí)行了task1

    • dispatch_once
      GCD只會也只能執(zhí)行一次該任務(wù)的方法驼抹,常用語單例創(chuàng)建中,在整個程序運行過程中只會執(zhí)行一次拜鹤。

      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
          //此處執(zhí)行單例創(chuàng)建方法
          NSLog(@"此段代碼只會執(zhí)行一次!");
      });
      

      此處結(jié)果不是很容易能看出來框冀,如果放到一個類的創(chuàng)建方法中,多次執(zhí)行就能很容易看到實際執(zhí)行的次數(shù)只有一次敏簿。

    • dispatch_apply
      GCD中快速迭代方法明也,有點類似dispatch_group_wait,會等待dispatch_apply中的全部任務(wù)執(zhí)行完畢惯裕。

      dispatch_queue_t queue = dispatch_queue_create("queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
      dispatch_apply(5, queue, ^(size_t i) {
          NSLog(@"current index:%zd --> %@",i,[NSThread currentThread]);
      });
      NSLog(@"end task %@",[NSThread currentThread]);//最后才會輸出此處代碼
      

      輸出結(jié)果為正常的串行/并行隊列中同步/異步任務(wù)調(diào)用順序温数,只不過需要等待 dispatch_apply 執(zhí)行完畢后才會執(zhí)行后續(xù)任務(wù)。

三蜻势、延伸思考

  • 在串行撑刺、并行隊列中,同步握玛、異步執(zhí)行任務(wù)時够傍,如果涉及到嵌套操作,那么其執(zhí)行的順序以及開啟的線程狀態(tài)與數(shù)量都有什么樣的結(jié)果呢挠铲?

  • 死鎖觸發(fā)有幾種情況冕屯?

    1. 主隊列中執(zhí)行同步任務(wù):
    dispatch_sync(dispatch_get_main_queue(), ^{
          NSLog(@"do something here");
    });
    

    雖然此任務(wù)是新開的一個同步任務(wù),處于主隊列中拂苹,但是實際上是嵌套在另一個主隊列同步任務(wù)中(當前正在執(zhí)行的任務(wù)中)安聘,當調(diào)用dispatch_sync方法時,會將此block加入到主隊列尾部醋寝,等待主隊列中的任務(wù)(當前正在執(zhí)行的任務(wù))執(zhí)行完畢返回后搞挣,才會繼續(xù)執(zhí)行block中的任務(wù)。

    根據(jù)規(guī)則音羞,串行隊列同步執(zhí)行任務(wù)會阻塞當前線程囱桨,直到該任務(wù)執(zhí)行完畢,此處當前線程為主線程嗅绰,那么調(diào)用block中的任務(wù)時主線程會被阻塞舍肠,意味著主隊列中的當前任務(wù)不能繼續(xù)執(zhí)行,而block中的任務(wù)必須等待主隊列中的當前任務(wù)執(zhí)行完畢才能繼續(xù)執(zhí)行窘面,進而形成了一個相互等待狀態(tài)翠语,線程就發(fā)生了死鎖。

    1. 同一個串行隊列中嵌套執(zhí)行同步任務(wù)
    dispatch_queue_t queue = dispatch_queue_create("queue.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
          NSLog(@"task1 --->%@",[NSThread currentThread]);
          dispatch_sync(queue, ^{
              NSLog(@"task2 --->%@",[NSThread currentThread]);
          });
    });
    

    此操作將會卡在task2輸出之前:

    task1 ---><NSThread: 0x604000079dc0>{number = 1, name = main}
    (lldb) //卡死
    

    此處所處情況與主隊列中執(zhí)行同步任務(wù)情況相同财边,只不過更加具體和明顯肌括。

    此處task1沒有被卡死,是因為隊列queue中沒有其他任務(wù)正在執(zhí)行酣难,那么task1任務(wù)加入到隊列queue后直接被執(zhí)行谍夭;當執(zhí)行到第二個dispatch_sync方法時黑滴,會將task2任務(wù)追加到隊列queue尾部,此時task1任務(wù)實際上并沒有執(zhí)行完畢紧索,但是因為調(diào)用了task2任務(wù)袁辈,那么此處task1任務(wù)所在線程將會阻塞等待task2任務(wù)執(zhí)行完畢,但是由于task1任務(wù)并未執(zhí)行返回結(jié)果珠漂,導致task2任務(wù)在此處同樣處于等待狀態(tài)晚缩。如此一來,2個任務(wù)相互等待對方執(zhí)行完畢媳危,直接導致死鎖荞彼。

    以上需要注意的是,所有觸發(fā)死鎖的同步任務(wù)都處于同一個串行隊列中济舆,異步任務(wù)在添加任務(wù)后不會等待任執(zhí)行完畢卿泽,而是繼續(xù)往下執(zhí)行莺债,所以無法觸相互等待狀態(tài)就不會發(fā)生死鎖狀態(tài)滋觉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市齐邦,隨后出現(xiàn)的幾起案子椎侠,更是在濱河造成了極大的恐慌,老刑警劉巖措拇,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件我纪,死亡現(xiàn)場離奇詭異,居然都是意外死亡丐吓,警方通過查閱死者的電腦和手機浅悉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來券犁,“玉大人术健,你說我怎么就攤上這事≌吵模” “怎么了荞估?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稚新。 經(jīng)常有香客問我勘伺,道長,這世上最難降的妖魔是什么褂删? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任飞醉,我火速辦了婚禮,結(jié)果婚禮上屯阀,老公的妹妹穿的比我還像新娘缅帘。我一直安慰自己噪裕,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布股毫。 她就那樣靜靜地躺著膳音,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铃诬。 梳的紋絲不亂的頭發(fā)上祭陷,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音趣席,去河邊找鬼兵志。 笑死,一個胖子當著我的面吹牛宣肚,可吹牛的內(nèi)容都是我干的想罕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼霉涨,長吁一口氣:“原來是場噩夢啊……” “哼按价!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笙瑟,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤楼镐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后往枷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體框产,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年错洁,在試婚紗的時候發(fā)現(xiàn)自己被綠了秉宿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡屯碴,死狀恐怖描睦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窿锉,我是刑警寧澤酌摇,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站嗡载,受9級特大地震影響窑多,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜洼滚,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一埂息、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦千康、人聲如沸享幽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽值桩。三九已至,卻和暖如春豪椿,著一層夾襖步出監(jiān)牢的瞬間奔坟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工搭盾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咳秉,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓鸯隅,卻偏偏與公主長得像澜建,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蝌以,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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