多线程

NSThread

这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。但是,它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 NSThread.currentThread(),它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。
在做定时器的时候也可以用用,只是用多线程启动,并不需要关心线程本身。

创建并启动

  • 先创建线程类,再启动
  let thread = NSThread(target: self, selector: "run:", object: nil)

  //启动
  thread.start()```
  
* 创建并自动启动

NSThread.detachNewThreadSelector("run:", toTarget: self, withObject: nil)

* 使用 NSObject 的方法创建并自动启动

self.performSelectorInBackground("run:", withObject: nil)

>Swift中,这个方法不推荐使用。

##其他方法

//取消线程
public func cancel()

//启动线程
public func start()

//判断某个线程的状态的属性
public var executing: Bool { get }
public var finished: Bool { get }
public var cancelled: Bool { get }

//设置和获取线程名字
public var name: String?

//获取当前线程信息
public class func currentThread() -> NSThread

//获取主线程信息
public class func mainThread() -> NSThread

//使当前线程暂停一段时间,或者暂停到某个时刻
public class func sleepForTimeInterval(ti: NSTimeInterval)
public class func sleepUntilDate(date: NSDate)


#NSOperation和NSOperationQueue
```NSOperation``` 是苹果公司对 ```GCD``` 的封装,完全面向对象。操作步骤:
1. 将要执行的任务封装到一个 ```NSOperation``` 对象中
。
2. 将此任务添加到一个 ```NSOperationQueue``` 对象中
。

##普通用法

//1.创建队列
let queue = NSOperationQueue()

//2.创建NSBlockOperation对象
let operation = NSBlockOperation { () -> Void in
NSLog("%@", NSThread.currentThread())
}

//3.添加多个Block
for i in 0..<5 {
operation.addExecutionBlock { () -> Void in
NSLog("第%ld次 - %@", i, NSThread.currentThread())
}
}

//4.队列添加任务
queue.addOperation(operation)```

多线程环境下,打印输出用NSLog(),不用print()

任务依赖

比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:

//1.任务一:下载图片
let operation1 = NSBlockOperation { () -> Void in
    NSLog("下载图片 - %@", NSThread.currentThread())
    NSThread.sleepForTimeInterval(1.0)
}

//2.任务二:打水印
let operation2 = NSBlockOperation { () -> Void in
    NSLog("打水印   - %@", NSThread.currentThread())
    NSThread.sleepForTimeInterval(1.0)
}

//3.任务三:上传图片
let operation3 = NSBlockOperation { () -> Void in
    NSLog("上传图片 - %@", NSThread.currentThread())
    NSThread.sleepForTimeInterval(1.0)
}

//4.设置依赖
operation2.addDependency(operation1)    //任务二依赖任务一
operation3.addDependency(operation2)    //任务三依赖任务二

//5.创建队列并加入任务
let queue = NSOperationQueue()
queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)

从其他线程回到主线程

NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in

}```

#GCD
它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。

##普通用法

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {

        //这里写需要大量时间的代码
        print("这里写需要大量时间的代码")
        
        dispatch_async(dispatch_get_main_queue(), {
            
            //这里返回主线程,写需要主线程执行的代码
            print("这里返回主线程,写需要主线程执行的代码")
        })
    }) 

## 创建队列
* 主队列

let queue = ispatch_get_main_queue()

* 自己创建的队列

//串行队列
let queue = dispatch_queue_create("tk.bourne.testQueue", nil);
let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL)
//并行队列
let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT)

* 全局并行队列

let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

>只要是并行任务一般都加入到这个队列。这是系统提供的一个并发队列。

##创建任务
* 同步任务:会阻塞当前线程 (SYNC)

dispatch_sync(<#queue#>, { () -> Void in
//code here
println(NSThread.currentThread())
})

异步任务:不会阻塞当前线程 (ASYNC)

dispatch_async(<#queue#>, { () -> Void in
//code here
println(NSThread.currentThread())
})

###示例一:
以下代码在主线程调用,结果是什么?

NSLog("之前 - %@", NSThread.currentThread())
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
NSLog("之后 - %@", NSThread.currentThread())

####答案:
只会打印第一句:```之前 - <NSThread: 0x7fb3a9e16470>{number = 1, name = main}``` ,然后主线程就卡死了,你可以在界面上放一个按钮,你就会发现点不了了。
####解释:
同步任务会阻塞当前线程,然后把 Block 中的任务放到指定的队列中执行,只有等到 Block 中的任务完成后才会让当前线程继续往下运行。
那么这里的步骤就是:打印完第一句后,```dispatch_sync``` 立即阻塞当前的主线程,然后把 Block 中的任务放到 ```main_queue``` 中,可是 ```main_queue``` 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以 Block 中的任务就不能完成,它不完成,```dispatch_sync``` 就会一直阻塞主线程,这就是死锁现象。导致主线程一直卡死。

###示例二:
以下代码会产生什么结果?

let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL)

NSLog("之前 - %@", NSThread.currentThread())

dispatch_async(queue, { () -> Void in
    NSLog("sync之前 - %@", NSThread.currentThread())
    dispatch_sync(queue, { () -> Void in
         NSLog("sync - %@", NSThread.currentThread())
    })
    NSLog("sync之后 - %@", NSThread.currentThread())

})

NSLog("之后 - %@", NSThread.currentThread())

####答案:
```2015-07-30 02:06:51.058 test[33329:8793087] 之前 - <NSThread: 0x7fe32050dbb0>{number = 1, name = main}
2015-07-30 02:06:51.059 test[33329:8793356] sync之前 - <NSThread: 0x7fe32062e9f0>{number = 2, name = (null)}
2015-07-30 02:06:51.059 test[33329:8793087] 之后 - <NSThread: 0x7fe32050dbb0>{number = 1, name = main}```
很明显 ```sync - %@``` 和 ```sync之后 - %@``` 没有被打印出来!这是为什么呢?我们再来分析一下:
####分析:

1. 使用 DISPATCH_QUEUE_SERIAL 这个参数,创建了一个 串行队列。

2. 打印出 ```之前 - %@ ```这句。

3. ```dispatch_async``` 异步执行,所以当前线程不会被阻塞,于是有了两条线程,一条当前线程继续往下打印出 ```之后 - %@```这句, 另一条执行 Block 中的内容打印 ```sync之前 - %@``` 这句。因为这两条是并行的,所以打印的先后顺序无所谓。

4. ```dispatch_sync```同步执行,于是它所在的线程会被阻塞,一直等到 ```sync``` 里的任务执行完才会继续往下。于是 ```sync``` 就把自己 Block 中的任务放到 ```queue``` 中,可 ```queue``` 是一个串行队列,一次执行一个任务,所以 ```sync``` 的 Block 必须等到前一个任务执行完毕,可是 ```queue``` 正在执行的任务就是被 ```sync``` 阻塞了的那个。于是又发生了死锁。所以 ```sync``` 所在的线程被卡死了。剩下的两句代码自然不会打印。

## 队列组
队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。下面是使用方法,这是一个很实用的功能。

//1.创建队列组
let group = dispatch_group_create()
//2.创建队列
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

//3.多次使用队列组的方法执行任务, 只有异步方法
//3.1.执行3次循环
dispatch_group_async(group, queue) { () -> Void in
for _ in 0..<3 {
NSLog("group-01 - %@", NSThread.currentThread())
}
}

//3.2.主队列执行8次循环
dispatch_group_async(group, dispatch_get_main_queue()) { () -> Void in
for _ in 0..<8 {
NSLog("group-02 - %@", NSThread.currentThread())
}
}

//3.3.执行5次循环
dispatch_group_async(group, queue) { () -> Void in
for _ in 0..<5 {
NSLog("group-03 - %@", NSThread.currentThread())
}
}

//4.都完成后会自动通知
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
NSLog("完成 - %@", NSThread.currentThread())
}


##从其他线程回到主线程

dispatch_async(dispatch_get_main_queue(), { () -> Void in

})```

延时执行

var time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(10 * NSEC_PER_SEC))
dispatch_after(time, globalQueue) { () -> Void in
    println("在10秒后执行")
} 

NSEC_PER_SEC表示的是秒数,它还提供了NSEC_PER_MSEC表示毫秒。

参考文献

一篇比较好的关于多线程的文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容

  • 在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案...
    张战威ican阅读 603评论 0 0
  • 在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案...
    伯恩的遗产阅读 274,462评论 251 2,332
  • 从哪说起呢? 单纯讲多线程编程真的不知道从哪下嘴。。 不如我直接引用一个最简单的问题,以这个作为切入点好了 在ma...
    Mr_Baymax阅读 2,752评论 1 17
  • 学习多线程,转载两篇大神的帖子,留着以后回顾!第一篇:关于iOS多线程,你看我就够了 第二篇:GCD使用经验与技巧...
    John_LS阅读 617评论 0 3
  • 岁月迢迢,往事遥遥。启书页,来诵灵韶。桑田易朽,诗韵难消。有汉时风,唐时律,宋时谣。 盛衰有序,天道何逃?繁华景,...
    徐啸阅读 268评论 0 0