iOS多線程 Swift4 GCD深入解析

iOS多線程深入解析

必要的概念

進(jìn)程/線程

進(jìn)程:進(jìn)程指在系統(tǒng)中能獨立運行并作為資源分配的基本單位御毅,它是由一組機器指令根欧、數(shù)據(jù)和堆棧等組成的,是一個能獨立運行的活動實體亚享。

線程:線程是進(jìn)程的基本執(zhí)行單元咽块,一個進(jìn)程(程序)的所有任務(wù)都在線程中執(zhí)行。

操作系統(tǒng)引入進(jìn)程的目的:為了使多個程序能并發(fā)執(zhí)行欺税,以提高資源的利用率和系統(tǒng)的吞吐量侈沪。

操作系統(tǒng)引入線程的目的:在操作系統(tǒng)中再引入線程,則是為了減少程序在并發(fā)執(zhí)行時所付出的時空開銷亭罪,使OS具有更好的并發(fā)性。多線程技術(shù)可以提高程序的執(zhí)行效率歼秽。

在引入線程的OS中应役,通常把進(jìn)程作為資源分配的基本單位,而把線程作為獨立運行和獨立調(diào)度的基本單位。

同步/異步

同步:多個任務(wù)情況下箩祥,一個任務(wù)A執(zhí)行結(jié)束院崇,才可以執(zhí)行另一個任務(wù)B。

異步:多個任務(wù)情況下袍祖,一個任務(wù)A正在執(zhí)行底瓣,同時可以執(zhí)行另一個任務(wù)B。任務(wù)B不用等待任務(wù)A結(jié)束才執(zhí)行蕉陋。存在多條線程捐凭。

并行/并發(fā)

并行:指兩個或多個事件在同一時刻發(fā)生。多核CUP同時開啟多條線程供多個任務(wù)同時執(zhí)行凳鬓,互不干擾茁肠。

并發(fā):指兩個或多個事件在同一時間間隔內(nèi)發(fā)生∷蹙伲可以在某條線程和其他線程之間反復(fù)多次進(jìn)行上下文切換垦梆,看上去就好像一個CPU能夠并且執(zhí)行多個線程一樣。其實是偽異步仅孩。

線程間通信

在1個進(jìn)程中奶赔,線程往往不是孤立存在的,多個線程之間需要經(jīng)常進(jìn)行通信

線程間通信的體現(xiàn):

  • 1個線程傳遞數(shù)據(jù)給另1個線程
  • 在1個線程中執(zhí)行完特定任務(wù)后杠氢,轉(zhuǎn)到另1個線程繼續(xù)執(zhí)行任務(wù)

多線程概念

多線程是指在軟件或硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)。通俗講就是在同步或異步的情況下另伍,開辟新線程鼻百,進(jìn)行線程間的切換,以及對線程進(jìn)行合理的調(diào)度摆尝,做到優(yōu)化提升程序性能的目的温艇。

多線程的優(yōu)點

  • 能適當(dāng)提高程序的執(zhí)行效率
  • 能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)
  • 避免在處理耗時任務(wù)時造成主線程阻塞

多線程的缺點

  • 開啟線程需要占用一定的內(nèi)存空間堕汞,如果開啟大量的線程勺爱,會占用大量的內(nèi)存空間,降低程序的性能
  • 線程越多讯检,CPU在調(diào)度線程上的開銷就越大
  • 可能會導(dǎo)致多個線程相互持續(xù)等待[死鎖]
  • 程序設(shè)計更加復(fù)雜:比如線程之間的通信琐鲁、多線程之間的數(shù)據(jù)競爭

GCD(Grand Central Dispatch)

Dispatch會自動的根據(jù)CPU的使用情況,創(chuàng)建線程來執(zhí)行任務(wù)人灼,并且自動的運行到多核上围段,提高程序的運行效率。對于開發(fā)者來說投放,在GCD層面是沒有線程的概念的奈泪,只有隊列(queue)。任務(wù)都是以block的方式提交到隊列上,然后GCD會自動的創(chuàng)建線程池去執(zhí)行這些任務(wù)涝桅。

GCD的優(yōu)點:

  • GCD是蘋果公司為多核的并行運算提出的解決方案
  • GCD會自動利用更多的CPU內(nèi)核(比如雙核拜姿、四核)
  • GCD會自動管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)冯遂、銷毀線程)
  • 程序員只需要告訴GCD想要執(zhí)行什么任務(wù)蕊肥,不需要編寫任何線程管理代碼

GCD中有兩個核心概念

任務(wù) block:執(zhí)行什么操作

隊列 queue:用來存放任務(wù)

GCD的使用就兩個步驟

  1. 定制任務(wù),確定想做的事情
  2. 將任務(wù)添加到隊列中债蜜,GCD會自動將隊列中的任務(wù)取出晴埂,放到對應(yīng)的線程中執(zhí)行。任務(wù)的取出遵循隊列的FIFO原則:先進(jìn)先出寻定,后進(jìn)后出儒洛。

GCD中有兩個執(zhí)行任務(wù)的函數(shù)

  • 同步執(zhí)行任務(wù)(sync

    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); 
    

    同步任務(wù)會阻塞當(dāng)前線程,然后把Block中的任務(wù)放到指定的隊列中執(zhí)行狼速,只有等到Block中的任務(wù)完成后才會讓當(dāng)前線程繼續(xù)往下運行琅锻。

    sync是一個強大但是容易被忽視的函數(shù)。使用sync向胡,可以方便的進(jìn)行線程間同步恼蓬。但是,有一點要注意僵芹,sync容易造成死鎖处硬。

  • 異步執(zhí)行任務(wù)(async

    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    

    異步任務(wù)會再開辟一個線程,當(dāng)前線程繼續(xù)往下走拇派,新線程去執(zhí)行block里的任務(wù)荷辕。

GCD的隊列可以分為兩大類型

  • 并行隊列(Concurrent Dispatch Queue)

    • 可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
    • 并行功能只有在異步(dispatch_async)函數(shù)下才有效

    放到并行隊列的任務(wù),如果是異步執(zhí)行件豌,GCD也會FIFO的取出來疮方,但不同的是,它取出來一個就會放到別的線程茧彤,然后再取出來一個又放到另一個的線程骡显。這樣由于取的動作很快,忽略不計曾掂,看起來惫谤,所有的任務(wù)都是一起執(zhí)行的。不過需要注意遭殉,GCD會根據(jù)系統(tǒng)資源控制并行的數(shù)量石挂,所以如果任務(wù)很多,它并不會讓所有任務(wù)同時執(zhí)行险污。

  • 串行隊列(Serial Dispatch Queue)

    讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后痹愚,再執(zhí)行下一個任務(wù))

    同步執(zhí)行 異步執(zhí)行
    串行隊列 當(dāng)前線程富岳,一個一個執(zhí)行 其他線程,一個一個執(zhí)行
    并發(fā)隊列 當(dāng)前線程拯腮,一個一個執(zhí)行 開很多線程窖式,一起執(zhí)行

Swift4 GCD 使用

DispatchQueue

最簡單的,可以按照以下方式初始化一個隊列

//這里的名字能夠方便開發(fā)者進(jìn)行Debug
let queue = DispatchQueue(label: "com.geselle.demoQueue")

這樣初始化的隊列是一個默認(rèn)配置的隊列动壤,也可以顯式的指明對列的其他屬性

let label = "com.leo.demoQueue"
let qos =  DispatchQoS.default
let attributes = DispatchQueue.Attributes.concurrent
let autoreleaseFrequency = DispatchQueue.AutoreleaseFrequency.never
let queue = DispatchQueue(label: label, qos: qos, attributes: attributes, autoreleaseFrequency: autoreleaseFrequency, target: nil)

這里萝喘,我們來一個參數(shù)分析他們的作用

  • label: 隊列的標(biāo)識符,方便調(diào)試
  • qos: 隊列的quality of service琼懊。用來指明隊列的“重要性”阁簸,后文會詳細(xì)講到。
  • attributes: 隊列的屬性哼丈。類型是DispatchQueue.Attributes,是一個結(jié)構(gòu)體启妹,遵循了協(xié)議OptionSet。意味著你可以這樣傳入第一個參數(shù)[.option1,.option2]醉旦。
    • 默認(rèn):隊列是串行的饶米。
    • .concurrent:隊列為并行的。
    • .initiallyInactive:則隊列任務(wù)不會自動執(zhí)行车胡,需要開發(fā)者手動觸發(fā)檬输。
  • autoreleaseFrequency: 顧名思義,自動釋放頻率匈棘。有些隊列是會在執(zhí)行完任務(wù)后自動釋放的丧慈,有些比如Timer等是不會自動釋放的,是需要手動釋放主卫。

隊列分類

  • 系統(tǒng)創(chuàng)建的隊列
    • 主隊列(對應(yīng)主線程)
    • 全局隊列
  • 用戶創(chuàng)建的隊列
// 獲取系統(tǒng)隊列
let mainQueue = DispatchQueue.main
let globalQueue = DispatchQueue.global()
let globalQueueWithQos = DispatchQueue.global(qos: .userInitiated)

// 創(chuàng)建串行隊列
let serialQueue = DispatchQueue(label: "com.geselle.serialQueue")

// 創(chuàng)建并行隊列
let concurrentQueue = DispatchQueue(label: "com.geselle.concurrentQueue",attributes:.concurrent)

// 創(chuàng)建并行隊列伊滋,并手動觸發(fā)
let concurrentQueue2 = DispatchQueue(label:"com.geselle.concurrentQueue2", qos: .utility,attributes[.concurrent,.initiallyInactive]) 
//手動觸發(fā)
if let queue = inactiveQueue {
    queue.activate()
}

suspend / resume

Suspend可以掛起一個線程,就是把這個線程暫停了队秩,它占著資源,但不運行昼浦。

Resume可以繼續(xù)掛起的線程馍资,讓這個線程繼續(xù)執(zhí)行下去。

concurrentQueue.resume()
concurrentQueue.suspend()

QoS(quality of service)

QoS的全稱是quality of service关噪。在Swift 3中鸟蟹,它是一個結(jié)構(gòu)體,用來制定隊列或者任務(wù)的重要性使兔。

何為重要性呢建钥?就是當(dāng)資源有限的時候,優(yōu)先執(zhí)行哪些任務(wù)虐沥。這些優(yōu)先級包括 CPU 時間熊经,數(shù)據(jù) IO 等等泽艘,也包括 ipad muiti tasking(兩個App同時在前臺運行)。

通常使用QoS為以下四種镐依,從上到下優(yōu)先級依次降低匹涮。

  • User Interactive: 和用戶交互相關(guān),比如動畫等等優(yōu)先級最高槐壳。比如用戶連續(xù)拖拽的計算
  • User Initiated: 需要立刻的結(jié)果然低,比如push一個ViewController之前的數(shù)據(jù)計算
  • Utility: 可以執(zhí)行很長時間,再通知用戶結(jié)果务唐。比如下載一個文件雳攘,給用戶下載進(jìn)度。
  • Background: 用戶不可見枫笛,比如在后臺存儲大量數(shù)據(jù)

通常吨灭,你需要問自己以下幾個問題

  • 這個任務(wù)是用戶可見的嗎?
  • 這個任務(wù)和用戶交互有關(guān)嗎崇堰?
  • 這個任務(wù)的執(zhí)行時間有多少沃于?
  • 這個任務(wù)的最終結(jié)果和UI有關(guān)系嗎?

在GCD中海诲,指定QoS有以下兩種方式

方式一:創(chuàng)建一個指定QoSqueue

let backgroundQueue = DispatchQueue(label: "com.geselle.backgroundQueue", qos: .background)
backgroundQueue.async {
    //在QoS為background下運行
}

方式二:在提交block的時候繁莹,指定QoS

queue.async(qos: .background) {
    //在QoS為background下運行
}

DispatchGroup

DispatchGroup用來管理一組任務(wù)的執(zhí)行,然后監(jiān)聽任務(wù)都完成的事件特幔。比如咨演,多個網(wǎng)絡(luò)請求同時發(fā)出去,等網(wǎng)絡(luò)請求都完成后reload UI蚯斯。

let group = DispatchGroup()

let queueBook = DispatchQueue(label: "book")
print("start networkTask task 1")
queueBook.async(group: group) {
    sleep(2)
    print("End networkTask task 1")
}
let queueVideo = DispatchQueue(label: "video")
print("start networkTask task 2")
queueVideo.async(group: group) {
    sleep(2)
    print("End networkTask task 2")
}

group.notify(queue: DispatchQueue.main) {
    print("all task done")
}

group.notify會等group里的所有任務(wù)全部完成以后才會執(zhí)行(不管是同步任務(wù)還是異步任務(wù))薄风。

Group.enter / Group.leave

/*
 首先寫一個函數(shù),模擬異步網(wǎng)絡(luò)請求
 
 這個函數(shù)有三個參數(shù)
 * label 表示id
 * cost 表示時間消耗
 * complete 表示任務(wù)完成后的回調(diào)

 */
public func networkTask(label:String, cost:UInt32, complete:@escaping ()->()){
    print("Start network Task task%@",label)
    DispatchQueue.global().async {
        sleep(cost)
        print("End networkTask task%@",label)
        DispatchQueue.main.async {
            complete()
        }
    }
}

// 我們模擬兩個耗時2秒和4秒的網(wǎng)絡(luò)請求

print("Group created")
let group = DispatchGroup()
group.enter()
networkTask(label: "1", cost: 2, complete: {
    group.leave()
})

group.enter()
networkTask(label: "2", cost: 2, complete: {
    group.leave()
})

group.wait(timeout: .now() + .seconds(4))

group.notify(queue: .main, execute:{
    print("All network is done")
})

Group.wait

DispatchGroup支持阻塞當(dāng)前線程拍嵌,等待執(zhí)行結(jié)果遭赂。

//在這個點,等待三秒鐘
group.wait(timeout:.now() + .seconds(3))

DispatchWorkItem

上文提到的方式横辆,我們都是以block(或者叫閉包)的形式提交任務(wù)撇他。DispatchWorkItem則把任務(wù)封裝成了一個對象。

比如狈蚤,你可以這么使用

let item = DispatchWorkItem { 
    //任務(wù)
}
DispatchQueue.global().async(execute: item)

也可以在初始化的時候指定更多的參數(shù)

let item = DispatchWorkItem(qos: .userInitiated, flags: [.enforceQoS,.assignCurrentContext]) { 
    //任務(wù)
}

* 第一個參數(shù)表示 QoS困肩。
* 第二個參數(shù)類型為 DispatchWorkItemFlags。指定這個任務(wù)的配飾信息
* 第三個參數(shù)則是實際的任務(wù) block

DispatchWorkItemFlags的參數(shù)分為兩組

  • 執(zhí)行情況

    • barrier
    • detached
    • assignCurrentContext
  • QoS覆蓋信息

    • noQoS //沒有 QoS
    • inheritQoS //繼承 Queue 的 QoS
    • enforceQoS //自己的 QoS 覆蓋 Queue

after(延遲執(zhí)行)

GCD可以通過asyncAfter來提交一個延遲執(zhí)行的任務(wù)

比如

let deadline = DispatchTime.now() + 2.0
print("Start")
DispatchQueue.global().asyncAfter(deadline: deadline) { 
    print("End")
}

延遲執(zhí)行還支持一種模式DispatchWallTime

let walltime = DispatchWallTime.now() + 2.0
print("Start")
DispatchQueue.global().asyncAfter(wallDeadline: walltime) { 
    print("End")
}

這里的區(qū)別就是

  • DispatchTime 的精度是納秒
  • DispatchWallTime 的精度是微秒

Synchronization 同步

通常脆侮,在多線程同時會對一個變量(比如NSMutableArray)進(jìn)行讀寫的時候锌畸,我們需要考慮到線程的同步。舉個例子:比如線程一在對NSMutableArray進(jìn)行addObject的時候靖避,線程二如果也想addObject,那么它必須等到線程一執(zhí)行完畢后才可以執(zhí)行潭枣。

實現(xiàn)這種同步有很多種機制

NSLock 互斥鎖

let lock = NSLock()
lock.lock()
//Do something
lock.unlock()

使用鎖有一個不好的地方就是:lockunlock要配對使用比默,不然極容易鎖住線程,沒有釋放掉卸耘。

sync 同步函數(shù)

使用GCD退敦,隊列同步有另外一種方式- sync,講屬性的訪問同步到一個queue上去蚣抗,就能保證在多線程同時訪問的時候侈百,線程安全。

class MyData{
    private var privateData:Int = 0
    private let dataQueue = DispatchQueue(label: "com.leo.dataQueue")
    var data:Int{
        get{
            return dataQueue.sync{ privateData }
        }
        set{
            dataQueue.sync { privateData = newValue}
        }
    }
}

Barrier 線程阻斷

假設(shè)我們有一個并發(fā)的隊列用來讀寫一個數(shù)據(jù)對象翰铡。如果這個隊列里的操作是讀的钝域,那么可以多個同時進(jìn)行。如果有寫的操作锭魔,則必須保證在執(zhí)行寫入操作時例证,不會有讀取操作在執(zhí)行,必須等待寫入完成后才能讀取迷捧,否則就可能會出現(xiàn)讀到的數(shù)據(jù)不對织咧。這個時候我們會用到 Barrier

barrier flag提交的任務(wù)能夠保證其在并行隊列執(zhí)行的時候漠秋,是唯一的一個任務(wù)笙蒙。(只對自己創(chuàng)建的隊列有效,對gloablQueue無效)

我們寫個例子來看看效果

let concurrentQueue = DispatchQueue(label: "com.leo.concurrent", attributes: .concurrent)
concurrentQueue.async {
    readDataTask(label: "1", cost: 3)
}

concurrentQueue.async {
    readDataTask(label: "2", cost: 3)
}
concurrentQueue.async(flags: .barrier, execute: {
    NSLog("Task from barrier 1 begin")
    sleep(3)
    NSLog("Task from barrier 1 end")
})

concurrentQueue.async {
    readDataTask(label: "2", cost: 3)
}

然后庆锦,看到Log

2017-01-06 17:14:19.690 Dispatch[15609:245546] Start data task1
2017-01-06 17:14:19.690 Dispatch[15609:245542] Start data task2
2017-01-06 17:14:22.763 Dispatch[15609:245546] End data task1
2017-01-06 17:14:22.763 Dispatch[15609:245542] End data task2
2017-01-06 17:14:22.764 Dispatch[15609:245546] Task from barrier 1 begin
2017-01-06 17:14:25.839 Dispatch[15609:245546] Task from barrier 1 end
2017-01-06 17:14:25.839 Dispatch[15609:245546] Start data task3
2017-01-06 17:14:28.913 Dispatch[15609:245546] End data task3

執(zhí)行的效果就是:barrier任務(wù)提交后捅位,等待前面所有的任務(wù)都完成了才執(zhí)行自身。barrier任務(wù)執(zhí)行完了后搂抒,再執(zhí)行后續(xù)執(zhí)行的任務(wù)艇搀。

Semaphore 信號量

DispatchSemaphore是傳統(tǒng)計數(shù)信號量的封裝,用來控制資源被多任務(wù)訪問的情況求晶。

簡單來說焰雕,如果我只有兩個usb端口,如果來了三個usb請求的話芳杏,那么第3個就要等待淀散,等待有一個空出來的時候,第三個請求才會繼續(xù)執(zhí)行蚜锨。

我們來模擬這一情況:

public func usbTask(label:String, cost:UInt32, complete:@escaping ()->()){
    print("Start usb task%@",label)
    sleep(cost)
    print("End usb task%@",label)
    complete()
}

let semaphore = DispatchSemaphore(value: 2)
let queue = DispatchQueue(label: "com.leo.concurrentQueue", qos: .default, attributes: .concurrent)

queue.async {
    semaphore.wait()
    usbTask(label: "1", cost: 2, complete: { 
        semaphore.signal()
    })
}

queue.async {
    semaphore.wait()
    usbTask(label: "2", cost: 2, complete: {
        semaphore.signal()
    })
}

queue.async {
    semaphore.wait()
    usbTask(label: "3", cost: 1, complete: {
        semaphore.signal()
    })
}

log

2017-01-06 15:03:09.264 Dispatch[5711:162205] Start usb task2
2017-01-06 15:03:09.264 Dispatch[5711:162204] Start usb task1
2017-01-06 15:03:11.338 Dispatch[5711:162205] End usb task2
2017-01-06 15:03:11.338 Dispatch[5711:162204] End usb task1
2017-01-06 15:03:11.339 Dispatch[5711:162219] Start usb task3
2017-01-06 15:03:12.411 Dispatch[5711:162219] End usb task3

Tips:在serial queue上使用信號量要注意死鎖的問題。感興趣的同學(xué)可以把上述代碼的queue改成serial的慢蜓,看看效果亚再。

參考文章:

iOS多線程與GCD 你看我就夠了

GCD精講(Swift 3)

iOS多線程-各種線程鎖的簡單介紹

關(guān)于iOS多線程,你看我就夠了

IOS開發(fā)之多線程詳解

Swift 3必看:從使用場景了解GCD新API

淺析iOS應(yīng)用開發(fā)中線程間的通信與線程安全問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晨抡,一起剝皮案震驚了整個濱河市氛悬,隨后出現(xiàn)的幾起案子则剃,更是在濱河造成了極大的恐慌,老刑警劉巖如捅,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棍现,死亡現(xiàn)場離奇詭異,居然都是意外死亡镜遣,警方通過查閱死者的電腦和手機己肮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悲关,“玉大人谎僻,你說我怎么就攤上這事≡⑷瑁” “怎么了艘绍?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長秫筏。 經(jīng)常有香客問我诱鞠,道長,這世上最難降的妖魔是什么这敬? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任航夺,我火速辦了婚禮,結(jié)果婚禮上鹅颊,老公的妹妹穿的比我還像新娘敷存。我一直安慰自己,他們只是感情好堪伍,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布锚烦。 她就那樣靜靜地躺著,像睡著了一般帝雇。 火紅的嫁衣襯著肌膚如雪涮俄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天尸闸,我揣著相機與錄音彻亲,去河邊找鬼。 笑死吮廉,一個胖子當(dāng)著我的面吹牛苞尝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宦芦,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宙址,長吁一口氣:“原來是場噩夢啊……” “哼幢踏!你這毒婦竟也來了身隐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎松申,沒想到半個月后糖荒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轿塔,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡墙杯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了丑搔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厦瓢。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖低匙,靈堂內(nèi)的尸體忽然破棺而出旷痕,到底是詐尸還是另有隱情,我是刑警寧澤顽冶,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布欺抗,位于F島的核電站,受9級特大地震影響强重,放射性物質(zhì)發(fā)生泄漏绞呈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一间景、第九天 我趴在偏房一處隱蔽的房頂上張望佃声。 院中可真熱鬧,春花似錦倘要、人聲如沸圾亏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽志鹃。三九已至,卻和暖如春泽西,著一層夾襖步出監(jiān)牢的瞬間曹铃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工捧杉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陕见,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓味抖,卻偏偏與公主長得像评甜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仔涩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

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

  • GCD (Grand Central Dispatch) :iOS4 開始引入忍坷,使用更加方便,程序員只需要將任務(wù)添...
    池鵬程閱讀 1,334評論 0 2
  • 1. GCD簡介 什么是GCD呢?我們先來看看百度百科的解釋簡單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 367評論 0 0
  • 程序中同步和異步是什么意思承匣?有什么區(qū)別? 解釋一:異步調(diào)用是通過使用單獨的線程執(zhí)行的锤悄。原始線程啟動異步調(diào)用韧骗,異步調(diào)...
    風(fēng)繼續(xù)吹0閱讀 1,033評論 1 2
  • NSThread 第一種:通過NSThread的對象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 801評論 0 3
  • 風(fēng)停止了/天很藍(lán)/白色的扇葉也不轉(zhuǎn)動/ 開始做夢/墜落/放下了武器/沒有佛/只有我/和一片虛無的安靜/ 等待泉水/...
    云雀兒閱讀 125評論 0 1