swift中多线程的使用方法
概述
多线程可能是每一个程序开发都会遇到的问题.在swift中,苹果并没有重新开发出一套线程框架,而是继续使用ObjectiveC原有的一套线程框架.
目前在swift中最常用的线程方案主要有NSThread,GCD,NSOperation&NSOperationQueue三套方案.所以,接下来会分别的描述下这些方案的使用方法以及例子.
NSThread
NSThread是苹果封装的一套完全面向对象的多线程框架.相比其他的两种方案,这种方案更轻量.你可以直接操作线程对象,很直观和方便.但是,由于NSThread只是一个轻量级的封装.所以需要自己管理线程的生命周期,线程同步。并且线程同步对数据的加锁会有一定的系统开销.因此这套方案使用的频率并不高.通常都是用来获取线程的信息
NSThread的创建
NSThread主要有两种直接创建的方式:
|
|
第一个方法是NSThread的类方法,可以直接创建并自动启动NSThread线程.
比如:
|
|
第二个方法是NSThread的构造函数,可以直接实例化一个NSThread实例,然后在需要的时候再调用start()方法启动线程.比如:
|
|
除了上述的两种直接创建的方式外,还有一种使用NSObject的方法创建并自动启动NSThread的方式.
那就是使用:
|
|
比如:
|
|
注意:这种方法在swift2.0中,才能使用swift调用.以前苹果一直以安全为由,禁止Swift调用,只能是ObjectiveC调用.
NSThread的其他方法
除了最常用的NSThread创建和启动的方法外.它还内置了一些很方便的方法.使用这些方法,能完整的控制线程的操作与信息.
|
|
具体的信息可以参见苹果的API
以上就是最简单的NSThread的介绍,更多复杂的使用方法,比如两个线程的同步,线程间的通信,线程的顺序执行等等,我会新开一篇文章来描述的.
GCD
全称Grand Central Dispatch.是苹果公司开发的一种多核并行运行的技术.它会更合理的使用现代多核CPU的内核.并且与NSThread不同的是,它能自动的管理线程的生命周期,不再需要我们来关心了,我们只需要关心线程的执行内容就可以了.
设计
GCD的工作原理是让程序多个平行队列的任务,根据可使用的处理资源,安排他们在任何当前可用的处理器内核上执行.
从这里就可以看出,GCD引入了两个非常重要的概念,那就是队列以及任务.
任务:即需要执行的动作.也就是Task.在GCD中是一个Block中文称为闭包.它封装了一段代码用来表示这个线程要干什么.
队列:用于存放任务的一个池子.GCD会不断的从队列中取未执行的任务,然后执行.在GCD中队列分为了同步队列和异步队列.他们的区别主要是在于在执行过程中会不会阻塞当前的线程,直到Block中的任务执行完毕.而不管是同步队列还是异步队列,GCD都是采用FIFO先进先出的方式来一个一个的取任务的.只是异步队列会把每一个取出来的任务放入一个新的线程中执行,然后马上又去取下一个任务.需要注意的是,异步队列能同时执行的任务是有限制的,GCD会根据系统资源自动的控制并行执行的数量,并不会把所有的任务都马上执行.
创建
在swift中队列的创建可以使用dispatch_queue_create函数.
它的方法签名是:
|
|
第一个参数是队列的标志.第二个参数是队列的属性,如果是同步队列那么就传入DISPATCH_QUEUE_SERIAL或者nil,如果是异步队列那么就传入DISPATCH_QUEUE_CONCURRENT
比如:
|
|
除了上述的手动创建队列外,GCD中还内置了两个全局的队列.
一个是
主队列,这是一个特殊的同步队列.主要是用来在UI上执行一些变化操作的.1let mainQueue = dispatch_get_main_queue()另一个是全局的异步队列,这个是系统预先提供的一个异步队列.我们可以直接使用.
1let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
执行
队列创建好了后,就可以开始执行任务了.
对应同步和异步两种,可以分别调用dispatch_sync和dispatch_async 来执行任务.
它们的方法签名是:
|
|
第一个参数就是创建的任务队列. 第二个参数就是表示任务的闭包.这个闭包通常都是()->Void的.
在调用这两个方法的时候,一定要有任务和队列的概念.一定要牢记在同一个队列中的任务必须一个一个的执行完毕然后才执行下一个.
比如:
|
|
这段代码在只会在控制台打印一句before sync.出现这种情况的原因在于调用了主队列用作了同步任务的执行线程. 那么在调用dispatch_sync方法的时候,这个方法会阻塞当前线程直到闭包执行完毕,而当前线程又正好是主线程.因此就会出现一种死锁的情况:在主线程的执行主队列同步任务,会把主线程先阻塞了,然后等待主线程的闭包任务完成.要解决这个问题也很简单,就是把任务的执行队列改为新生成的一个队列let queue = dispatch_queue_create("1231", nil)而不是使用主队列即可.
同样的,执行同步任务的嵌套也有可能会出现死锁的情况:
|
|
这段代码也只会在控制台打印出before sync1,print("in sync1") 两句.原因同样是因为在sync1中调用dispatch_sync会把queue队列线程给阻塞了.然后等待sync2在queue队列线程上执行完毕,这样就造成了死锁.
组执行
在GCD中还可以把一些任务添加到一个任务组里去.这样就能实现监听一组任务是否完成.然后完成后通知执行其他的操作.
组操作的方法签名:
|
|
线程组的执行没有同步的方式,只有异步的方式.其实也很好理解,如果是同步的方式也就不存在线程组的通知了,反正都是顺序执行的.
下面是线程组调用的例子:
|
|
当执行完async1 async2 async3 后,会执行notify中的任务
Barrier执行
在GCD中还提供了两个阻塞队列执行的方法.
|
|
这两个方法体现了队列的FIFO先进先出的原则:当它前面的任务执行结束后它才会执行,而且在它后面的任务要等它执行了后再执行.换句话说,这个方法会阻塞这个queue队列(注意是阻塞队列的任务,而不是阻塞当前线程),等到这个队列中排在它之前的任务全部执行完成后,再执行自己的任务.而后取消任务的阻塞.使这个队列中后面的任务继续异步或同步的执行.需要注意的是,如果传入的队列类型不是DISPATCH_QUEUE_CONCURRENT,那么这个方法和普通的dispatch_async或dispatch_sync没区别.
比如:
|
|
NSOperation&NSOperationQueue
NSOperation是苹果公司在GCD之上推出的一个多线程框架,是对GCD的操作做了一层封装.它面向对象的提供了对象的操作,这对我们使用起来更好的理解了.特别是从JAVA .NET这些纯粹面向对象的语言转过来的,更容易使用了.
既然是对GCD的封装,那么GCD中最重要的两个概念任务与队列.同样在这套方案中是有实现的.
- 任务对应的是
NSOperation,即要执行的任务封装到一个NSOperation实例中. - 队列对应的是
NSOperationQueue,即执行的任务会添加到一个NSOperationQueue实例中.
创建任务
由于NSOperation是一个抽象类,因此创建任务主要由两种方式:
- 实例化它的子类
NSBlockOperation.然后调用start()启动任务.它会默认在当前队列中同步执行. - 继承
NSOperation,自己写实现. 其实如果熟悉JAVA的人,就能发现NSOperation和JAVA中的Runnable接口非常的类似,和Runnable接口的run()方法类似,NSOperation类也有一个main方法被设计用来扩展的.只要继承至这个类,然后重写main方法就可以了.
比如:
|
|
另外对于NSBlockOperation任务,除了在初始化的时候可以传入一个闭包外.还可以在start方法前,调用func addExecutionBlock(block: () -> Void)来增加执行的闭包.这些增加的闭包,会在调用start方法时,并发的执行
除此之外,NSOperation可以通过调用func addDependency(op: NSOperation)和func removeDependency(op: NSOperation)增加或删除依赖.只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。这样就能控制并发线程执行下的任务顺序
创建队列
NSOperationQueue是用来创建执行队列的. 因为在默认的情况下NSOperation的start()方法会在当前线程 同步执行的,也就是说会阻塞当前线程执行任务. 要想在其他线程中执行任务的话,这就需要使用NSOperationQueue了.
创建执行队列的方法也很简单,就是调用NSOperationQueue的构造函数初始化一个NSOperationQueue实例即可.
初始化以后,直接调用func addOperation(op: NSOperation)即可添加和自动执行任务了.
比如:
|
|
需要注意的是,如果把上面的注释去掉,那么程序就会报错:operation is finished and cannot be enqueued.也就是说执行过一次的任务就不能再一次加入到operationQueue中了.
另外一个需要注意的地方就是:NSOperationQueue是不区分同步还是异步队列的.它默认都是异步的.你只要设置最大并发数maxConcurrentOperationCount就可以了.如果你设置成1,那自然就是同步执行的了.
总结
以上就是Swift中多线程的基本用法,掌握这些知识对于开发一个高性能的应用非常重要.
更多的信息可以参考下官方文档.
而更多的用法我会另开文章来说明.