GCD源碼解析

GCD源碼解析

在我們開(kāi)發(fā)過(guò)程中煤伟,我們使用到多線程的時(shí)候,最多的應(yīng)該是使用GCD,因?yàn)樗褂闷饋?lái)非常的簡(jiǎn)潔便锨,簡(jiǎn)單围辙。
平常中,我們使用最多的放案,
dispatch_queue_create,
dispatch_get_global_queue,
dispatch_sync,
dispatch_async,
dispatch_group_create,
dispatch_once等姚建。可是吱殉,我們是否知道這些背后是做了什么事情掸冤,它和我們的內(nèi)部線程到底是什么關(guān)系,或者友雳,他們直接是否有直接的關(guān)系稿湿?我依次來(lái)做解答

dispatch_sync

dispatch_syncdispatch_async是相對(duì)的,一個(gè)是同步任務(wù)押赊,需要等待任務(wù)完成饺藤,一個(gè)異步不等待任務(wù)完成就返回。兩種不同的方式流礁,首先我們先介紹dispatch_sync.

dispatch_sync: 它一般情況下是不會(huì)重啟一個(gè)新的線程涕俗,而是在當(dāng)前線程執(zhí)行,注意神帅,這里說(shuō)的是當(dāng)前線程再姑,而不是主線程。當(dāng)然里面有特殊的存在get_main_queue,它是在主線程執(zhí)行的找御。當(dāng)我們調(diào)用dispatch_sync元镀,也就是同步任務(wù)的時(shí)候,是分兩種隊(duì)列的萎坷,一種就是串行隊(duì)列凹联,一種是并發(fā)隊(duì)列。在我們解析dispatch_sync源碼的之前哆档,我們先寫(xiě)幾個(gè)例子,并且住闯,把調(diào)用棧給寫(xiě)出來(lái)瓜浸,這樣更加有利于我們分析源碼。

// 串行隊(duì)列同步執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_SERIAL);//創(chuàng)建的是串行隊(duì)列
    dispatch_sync(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
    });
    dispatch_sync(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"222");
    });

// 下面是運(yùn)行結(jié)果
 myDemo[41725:1739115] current thread = <NSThread: 0x60400006cb80>{number = 1, name = main}
 myDemo[41725:1739115] 111
 myDemo[41725:1739115] current thread = <NSThread: 0x60400006cb80>{number = 1, name = main}
 myDemo[41725:1739115] 222

首先我們把此demo的堆棧貼出來(lái)


image.png

通過(guò)這個(gè)demo比原,就是按照我們所理解的串行執(zhí)行插佛,線程也是在主線程,因?yàn)槲覀儽旧砭褪悄J(rèn)在主線程運(yùn)行量窘,就像上面講的:dispatch_sync只是在當(dāng)前線程執(zhí)行

// 并行隊(duì)列的同步執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
        [NSThread sleepForTimeInterval:5];
    });
    dispatch_sync(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"222");
    });
    // 運(yùn)行結(jié)果
     myDemo[82272:3241865] current thread = <NSThread: 0x60000006f900>{number = 1, name = main}
     myDemo[82272:3241865] 111
     myDemo[82272:3241865] current thread = <NSThread: 0x60000006f900>{number = 1, name = main}
     myDemo[82272:3241865] 222

堆棧隊(duì)列


image.png

通過(guò)這個(gè)demo雇寇,我可以看到,即便是用的并行隊(duì)列,依然還是在主線程锨侯,并沒(méi)有重啟一個(gè)新的線程嫩海,并且是串行執(zhí)行。
說(shuō)明囚痴,我一開(kāi)始總結(jié)的是對(duì)的叁怪,執(zhí)行dispatch_sync,并沒(méi)有重啟線程深滚,即便是并行隊(duì)列奕谭。下面再看其他的demo

dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_SERIAL);// 創(chuàng)建串行隊(duì)列
    dispatch_sync(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
        dispatch_sync(queue01, ^{
            NSLog(@"current thread = %@", [NSThread currentThread]);
            NSLog(@"222");
        });
    });
    // 此處就會(huì)死鎖造成crash.
    
    // 我們來(lái)看另外一個(gè)例子
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_CONCURRENT);// 創(chuàng)建并行隊(duì)列
    dispatch_sync(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
        dispatch_sync(queue01, ^{
            NSLog(@"current thread = %@", [NSThread currentThread]);
            NSLog(@"222");
        });
    });
    // 執(zhí)行結(jié)果
myDemo[84905:3394929] current thread = <NSThread: 0x600000262e00>{number = 1, name = main}
myDemo[84905:3394929] 111
myDemo[84905:3394929] current thread = <NSThread: 0x600000262e00>{number = 1, name = main}
myDemo[84905:3394929] 222
// 執(zhí)行結(jié)果依然在主線程

通過(guò)這兩個(gè)例子的比較,只有在同步執(zhí)行痴荐,串行隊(duì)列的情況下血柳,才會(huì)造成死鎖。使用并行隊(duì)列的時(shí)候生兆,同步執(zhí)行难捌,依然沒(méi)問(wèn)題,并且依然在主線程皂贩。下面栖榨,我們上面那個(gè)例子的堆棧信息截屏下來(lái)


image.png
    // 不是通過(guò)create queue的方式,創(chuàng)建全局的并行隊(duì)列
    dispatch_queue_t queue01 = dispatch_get_global_queue(0, 0);
    dispatch_sync(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"222");
    });
    // 執(zhí)行結(jié)果
    myDemo[1637:4224206] current thread = <NSThread: 0x604000076700>{number = 1, name = main}
    myDemo[1637:4224206] 222

這種方式執(zhí)行的是_dispatch_sync_function_invoke
堆棧:

image.png

我根據(jù)上面3中堆棧信息明刷,來(lái)分析dispatch_sync源碼執(zhí)行婴栽,解答為什么如此執(zhí)行

dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
{
    if (unlikely(_dispatch_block_has_private_data(work))) {
        return _dispatch_sync_block_with_private_data(dq, work, 0);
    }
    dispatch_sync_f(dq, work, _dispatch_Block_invoke(work));
}

DISPATCH_NOINLINE
void
dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{
    if (likely(dq->dq_width == 1)) {
        // 串行隊(duì)列執(zhí)行到這里
        return dispatch_barrier_sync_f(dq, ctxt, func);
    }

    // Global concurrent queues and queues bound to non-dispatch threads
    // always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
    if (unlikely(!_dispatch_queue_try_reserve_sync_width(dq))) {
    // 通過(guò)下面的堆棧,當(dāng)創(chuàng)建全局并行隊(duì)列的時(shí)候辈末,才會(huì)執(zhí)行到此方法
        return _dispatch_sync_f_slow(dq, ctxt, func, 0);
    }

    _dispatch_introspection_sync_begin(dq);
    if (unlikely(dq->do_targetq->do_targetq)) {
        return _dispatch_sync_recurse(dq, ctxt, func, 0);
    }
    // 通過(guò)上面并行隊(duì)列堆棧發(fā)現(xiàn)dispatch_queue_create創(chuàng)建的并且隊(duì)列愚争,會(huì)調(diào)用此方法
    _dispatch_sync_invoke_and_complete(dq, ctxt, func);
}

DISPATCH_NOINLINE
void
dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func)
{
    dispatch_tid tid = _dispatch_tid_self();

    // The more correct thing to do would be to merge the qos of the thread
    // that just acquired the barrier lock into the queue state.
    //
    // However this is too expensive for the fastpath, so skip doing it.
    // The chosen tradeoff is that if an enqueue on a lower priority thread
    // contends with this fastpath, this thread may receive a useless override.
    //
    // Global concurrent queues and queues bound to non-dispatch threads
    // always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
    if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(dq, tid))) {
        return _dispatch_sync_f_slow(dq, ctxt, func, DISPATCH_OBJ_BARRIER_BIT);
    }

    _dispatch_introspection_sync_begin(dq);
    if (unlikely(dq->do_targetq->do_targetq)) {
        return _dispatch_sync_recurse(dq, ctxt, func, DISPATCH_OBJ_BARRIER_BIT);
    }
    // 串行隊(duì)列的時(shí)候,執(zhí)行此方法
    _dispatch_queue_barrier_sync_invoke_and_complete(dq, ctxt, func);
}

通過(guò)上面串行隊(duì)列的堆棧挤聘,我看到執(zhí)行到此方法_dispatch_queue_barrier_sync_invoke_and_complete

DISPATCH_NOINLINE
static void
_dispatch_queue_barrier_sync_invoke_and_complete(dispatch_queue_t dq,
        void *ctxt, dispatch_function_t func)
{
    // 首先先執(zhí)行此方法
    _dispatch_sync_function_invoke_inline(dq, ctxt, func);
    if (unlikely(dq->dq_items_tail || dq->dq_width > 1)) {
        return _dispatch_queue_barrier_complete(dq, 0, 0);
    }

    // Presence of any of these bits requires more work that only
    // _dispatch_queue_barrier_complete() handles properly
    //
    // Note: testing for RECEIVED_OVERRIDE or RECEIVED_SYNC_WAIT without
    // checking the role is sloppy, but is a super fast check, and neither of
    // these bits should be set if the lock was never contended/discovered.
    const uint64_t fail_unlock_mask = DISPATCH_QUEUE_SUSPEND_BITS_MASK |
            DISPATCH_QUEUE_ENQUEUED | DISPATCH_QUEUE_DIRTY |
            DISPATCH_QUEUE_RECEIVED_OVERRIDE | DISPATCH_QUEUE_SYNC_TRANSFER |
            DISPATCH_QUEUE_RECEIVED_SYNC_WAIT;
    uint64_t old_state, new_state;

    // similar to _dispatch_queue_drain_try_unlock
    os_atomic_rmw_loop2o(dq, dq_state, old_state, new_state, release, {
        new_state  = old_state - DISPATCH_QUEUE_SERIAL_DRAIN_OWNED;
        new_state &= ~DISPATCH_QUEUE_DRAIN_UNLOCK_MASK;
        new_state &= ~DISPATCH_QUEUE_MAX_QOS_MASK;
        if (unlikely(old_state & fail_unlock_mask)) {
            os_atomic_rmw_loop_give_up({
                return _dispatch_queue_barrier_complete(dq, 0, 0);
            });
        }
    });
    if (_dq_state_is_base_wlh(old_state)) {
        _dispatch_event_loop_assert_not_owned((dispatch_wlh_t)dq);
    }
}

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sync_function_invoke_inline(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func)
{
    dispatch_thread_frame_s dtf;
    _dispatch_thread_frame_push(&dtf, dq);
    // 在我們的串行隊(duì)列中轰枝,最終執(zhí)行到此方法
    _dispatch_client_callout(ctxt, func);
    _dispatch_perfmon_workitem_inc();
    _dispatch_thread_frame_pop(&dtf);
}

并行隊(duì)列串行執(zhí)行的時(shí)候,會(huì)執(zhí)行_dispatch_sync_invoke_and_complete

DISPATCH_NOINLINE
static void
_dispatch_sync_invoke_and_complete(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func)
{
    _dispatch_sync_function_invoke_inline(dq, ctxt, func);
    _dispatch_queue_non_barrier_complete(dq);
}

最終依然會(huì)執(zhí)行到_dispatch_client_callout
通過(guò)上面的源碼的分析组去,在執(zhí)行dispatch_sync的時(shí)候鞍陨,并不會(huì)創(chuàng)建新的線程。

dispatch_async

dispatch_async運(yùn)用了底層線程池从隆,會(huì)在和當(dāng)前線程不同的線程上處理任務(wù)诚撵。同一個(gè)串行隊(duì)列在異步執(zhí)行,會(huì)在同一個(gè)線程中键闺。不同的串行隊(duì)列寿烟,異步執(zhí)行,才會(huì)重啟一個(gè)線程辛燥。但是并行隊(duì)列筛武,每次都會(huì)重啟一個(gè)線程缝其。
首先,我依然還是通過(guò)幾個(gè)例子徘六,還看看什么效果内边,并且調(diào)出他們的堆棧.

// 串行隊(duì)列異步執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
    });

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

myDemo[38842:5876694] current thread = <NSThread: 0x600000278b40>{number = 3, name = (null)}
myDemo[38842:5876694] 111

調(diào)用堆棧:


image.png
// 異步隊(duì)列異步執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
    });

// 執(zhí)行結(jié)果
myDemo[39184:5895013] current thread = <NSThread: 0x604000475780>{number = 3, name = (null)}
myDemo[39184:5895013] 111

調(diào)用堆棧:


image.png
// 創(chuàng)建全局異步隊(duì)列
dispatch_queue_t queue01 = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
    });

// 執(zhí)行結(jié)果
myDemo[39245:5898319] current thread = <NSThread: 0x600000466640>{number = 3, name = (null)}
myDemo[39245:5898319] 111

調(diào)用堆棧:


image.png

通過(guò)上面的代碼顯示,無(wú)論是我們創(chuàng)建的時(shí)候串行隊(duì)列硕噩,還是異步隊(duì)列假残,或者是全局隊(duì)列,只要異步執(zhí)行炉擅,都會(huì)創(chuàng)建新的線程辉懒,只不過(guò)調(diào)用堆棧,有些許不同谍失,后面眶俩,我們講解代碼的時(shí)候,會(huì)根據(jù)堆棧來(lái)講解快鱼。
下面颠印,我來(lái)看一下,其他的情況:

dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_SERIAL);
 dispatch_async(queue01, ^{
        sleep(5);
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
    });
    dispatch_async(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"222");
    });
    
// 執(zhí)行結(jié)果
myDemo[39545:5914239] current thread = <NSThread: 0x60000027cb40>{number = 3, name = (null)}
myDemo[39545:5914239] 111
myDemo[39545:5914239] current thread = <NSThread: 0x60000027cb40>{number = 3, name = (null)}
myDemo[39545:5914239] 222

同一個(gè)串行隊(duì)列異步執(zhí)行抹竹,會(huì)在同一個(gè)異步線程中线罕,串行執(zhí)行

dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue01, ^{
        sleep(5);
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
    });
    dispatch_async(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"222");
    });
// 執(zhí)行結(jié)果

myDemo[39642:5919537] current thread = <NSThread: 0x600000265bc0>{number = 3, name = (null)}
myDemo[39642:5919537] 222
myDemo[39642:5919535] current thread = <NSThread: 0x60000007e700>{number = 4, name = (null)}
myDemo[39642:5919535] 111

同一個(gè)并行隊(duì)列,異步執(zhí)行窃判,會(huì)分別生成兩個(gè)不同的線程钞楼。相當(dāng)于是每一次的并行隊(duì)列,異步執(zhí)行袄琳,都會(huì)重啟一個(gè)線程執(zhí)行

// 全局隊(duì)列
dispatch_queue_t queue01 = dispatch_get_global_queue(0, 0);
dispatch_async(queue01, ^{
        sleep(5);
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"111");
    });
    dispatch_async(queue01, ^{
        NSLog(@"current thread = %@", [NSThread currentThread]);
        NSLog(@"222");
    });
// 執(zhí)行結(jié)果
myDemo[39753:5926161] current thread = <NSThread: 0x6040002656c0>{number = 3, name = (null)}
myDemo[39753:5926161] 222
myDemo[39753:5926162] current thread = <NSThread: 0x600000460fc0>{number = 4, name = (null)}
myDemo[39753:5926162] 111

可以上面的是同樣的結(jié)果询件。

異步源碼的分析

通過(guò)上面的串行隊(duì)列異步執(zhí)行的堆棧,我們清楚分別執(zhí)行:

_dispatch_queue_push->start_wqthread->_dispatch_call_block_and_release

#ifdef __BLOCKS__
void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DISPATCH_OBJ_CONSUME_BIT;

    _dispatch_continuation_init(dc, dq, work, 0, 0, dc_flags);
    _dispatch_continuation_async(dq, dc);
}

持續(xù)更新唆樊。宛琅。。逗旁。

GitHub地址:https://github.com/BernardChina/BCStudy/blob/master/GCD%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嘿辟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子片效,更是在濱河造成了極大的恐慌仓洼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堤舒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡哺呜,警方通過(guò)查閱死者的電腦和手機(jī)舌缤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人国撵,你說(shuō)我怎么就攤上這事陵吸。” “怎么了介牙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵壮虫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我环础,道長(zhǎng)囚似,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任线得,我火速辦了婚禮饶唤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贯钩。我一直安慰自己募狂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布角雷。 她就那樣靜靜地躺著祸穷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪勺三。 梳的紋絲不亂的頭發(fā)上雷滚,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音檩咱,去河邊找鬼揭措。 笑死,一個(gè)胖子當(dāng)著我的面吹牛刻蚯,可吹牛的內(nèi)容都是我干的绊含。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼炊汹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼躬充!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起讨便,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤充甚,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后霸褒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體伴找,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年废菱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了技矮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抖誉。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖衰倦,靈堂內(nèi)的尸體忽然破棺而出袒炉,到底是詐尸還是另有隱情,我是刑警寧澤樊零,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布我磁,位于F島的核電站,受9級(jí)特大地震影響驻襟,放射性物質(zhì)發(fā)生泄漏夺艰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一塑悼、第九天 我趴在偏房一處隱蔽的房頂上張望劲适。 院中可真熱鬧,春花似錦厢蒜、人聲如沸霞势。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)愕贡。三九已至,卻和暖如春巷屿,著一層夾襖步出監(jiān)牢的瞬間固以,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工嘱巾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留憨琳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓旬昭,卻偏偏與公主長(zhǎng)得像篙螟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子问拘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,392評(píng)論 8 265
  • 從哪說(shuō)起呢遍略? 單純講多線程編程真的不知道從哪下嘴。骤坐。 不如我直接引用一個(gè)最簡(jiǎn)單的問(wèn)題绪杏,以這個(gè)作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,762評(píng)論 1 17
  • iOS多線程編程 基本知識(shí) 1. 進(jìn)程(process) 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,就是一段程序的執(zhí)...
    陵無(wú)山閱讀 6,052評(píng)論 1 14
  • 我喜歡你 當(dāng)星期一和星期四的下課鈴響起 我的微笑就禁不住要洋溢 像小鳥(niǎo)張開(kāi)了翅膀 像白云舒展開(kāi)雙臂 我喜歡你 當(dāng)一...
    竹子LHM閱讀 387評(píng)論 2 3
  • “走在鄉(xiāng)間的小路上纽绍,暮歸的老牛是我同伴蕾久,藍(lán)天配朵夕陽(yáng)在胸膛,繽紛的云彩是晚霞的衣裳……”每次聽(tīng)到這首《鄉(xiāng)間的小路》...
    夢(mèng)在雨巷閱讀 414評(píng)論 1 1