多线程

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())

####答案:
只会打印第一句:```之前 - {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] 之前 - {number = 1, name = main}
2015-07-30 02:06:51.059 test[33329:8793356] sync之前 - {number = 2, name = (null)}
2015-07-30 02:06:51.059 test[33329:8793087] 之后 - {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表示毫秒。

参考文献

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

你可能感兴趣的:(多线程)