我们日常开发中经常会碰到有某种不(keng)错(die)的必须要用到多线程技术来解决的需求, 但是多线程如何使用呢? 我们今天来简单的了解一下.
使用多线程的几种方式
NSThread
这种方式可以开辟子线程去执行方法选择器中的方法.
[self performSelectorInBackground:@selector(doWork) withObject:nil];
[self performSelectorInBackground:@selector(doWork1) withObject:nil];
这两行代码开辟了两条子线程, 两条子线程分别执行doWork1和doWork两个方法, 用这种方法开辟出来的子线程是并行的, 这两个方法同时执行.
[self performSelectorOnMainThread:@selector(doMainWork) withObject:nil waitUntilDone:YES];
这个方法用于回到主线程执行某一个方法.
参数1: 方法选择器.
参数2: 传递的参数 如果这个参数被提前释放, 那么到了主线程中是空的.
参数3: 当前线程是否需要被阻塞, 直到主线程的doMainWork方法里的代码执行完. 如果主线程当前忙于其他事情, 就算调用这个方法, 也不会执行.
NSOperationQueue
本篇文章主要讨论GCD, 这个方法不进行说明
Grand Central Dispatch (GCD)
说到GCD, 首先有一个叫dispatch_queue_t的类, 这个类的一个对象是一个队列, 我们对这个队列进行操作(例如向这个队列追加一个任务), 系统会自动的把这个任务分配给一个线程去操作, 具体分配给哪个线程, 我们后面会进行说明.
GCD的使用, 无非就是我们向几个这样的队列里面追加各种各样的任务并且对这些任务进行一些必要的设置, 接下来系统会对这些任务按照我们之前的设定进行分配.
dispatch_queue_t 有两种队列 一种是serial dispatch queue(等待现在执行中处理, 就是串行队列), 这个队列会一次只处理一个任务, 这个任务结束后才处理下一个. 另一种是concurrent dispatch queue(不等待现在执行中处理, 就是并行队列), 这种队列会同时处理几个任务, 分别给这几个任务分配给不同的线程, 同时进行处理.
首先, 我们要自己生成一个serial队列:
参数1: 这个队列的名字
参数2: 当前队列的类型, 0是serial队列
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("serialQueue", 0);
上面这行代码就生成了一个serial队列,现在我们可以向这个队列中添加一个任务.
参数1: 向哪一个队列增加任务.
参数2: 任务是什么(block)
dispatch_async(mySerialDispatchQueue, ^{
NSLog(@"一个任务");
});
这样我们就在mySerialDispatchQueue这个队列中添加了一个任务,系统会自动为这个任务分配一个线程去执行.
注意:这个方法是向队列中添加一个任务! 是队列!而不是线程. 不要有一种错误的理解(向这个线程中添加了任务 这是错误的).
除了这个方法,还有另一种方法可以向队列中追加一个任务:
dispatch_sync(mySerialDispatchQueue, ^{
NSLog(@"一个任务");
});
这个方法比上面那个少了一个a字母,sync这个方法是同步方法, 当前线程会等待这个追加任务完成才会继续执行, async这个方法是异步方法, 当前线程不会等待这个追加任务完成就会继续执行.
我们一般用async, sync用的比较少, 原因是如果你对sync操作不正确, 就可能会产生线程死锁的情况(例如:在主线程用sync, 又在主线程里追加了一个任务, 这个时候由于主线程是serial串行队列, 会等待这段代码的执行完毕, 而这个任务又在等待主线程, 就会出现线程死锁).
dispatch_queue_t mainQueue = dispatch_get_main_queue();
这样我们就获得了主队列.我们可以向主队列追加一些任务了:
dispatch_async(mainQueue, ^{
});
我们也可以采用另一种简便写法:
dispatch_async(dispatch_get_main_queue(), ^{
});
主队列是serial队列.
刚才说过除了serial队列之外, 还有另一种队列 concurrent队列(并行队列).
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("concurrentQueue1", DISPATCH_QUEUE_CONCURRENT);
这个方法我们就创建了一个concurrent队列, 向concurrent队列追加任务的方法和serial一样.
dispatch_sync(myConcurrentDispatchQueue, ^{
NSLog(@"任务A");
});
dispatch_sync(myConcurrentDispatchQueue, ^{
NSLog(@"任务B");
});
dispatch_sync(myConcurrentDispatchQueue, ^{
NSLog(@"任务C");
});
dispatch_sync(myConcurrentDispatchQueue, ^{
NSLog(@"任务D");
});
现在我向这个队列中追加了四个任务, 假如当前设备最多只能开三条线程, 那么可能会使A B C三个任务同时执行, D任务暂时处于等待状态, 如果B任务执行的时间短, B任务结束的同时, D任务进入B线程里去执行.
其实我们并不用自己去手动创建concurrent队列, 系统给我们提供了几个concurrent队列.我们只需要获取到系统提供给我们的concurrent队列就可以了,
参数1: 队列优先级
dispatch_queue_t globalHighPriority = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
我们用这个方法就可以获取到系统给我们提供的concurrent队列, 这几个队列是单例, 我们在程序的任何地方调用都可以调用到, 我们可以尝试着打印一下这个队列的地址, 会发现地址都一样.
我们来研究一下第一个参数(队列优先级)的作用和用法. 系统给我们提供了四种优先级, 分别是 HIGH(高优先级) DEFAULT(默认优先级) LOW(低优先级) BACKGROUND(后台优先级) 按照从高到低排序.
现在我们想追加两个任务, 一个追加到HIGH优先级的队列中, 一个追加到后台优先级的队列中, 在多核情况下, 这两个任务是同时进行处理的,那在什么情况下才能看出优先级的作用呢? 当我们有十个任务要进行多线程操作. 其中的两个我想要最先执行, 那么我把这两个任务追加进高优先级队列, 把其他的追加进低优先级队列, 那么系统在分配线程的时候, 肯定会优先分配这两个任务.
如果我们不进行优先级操作, 所有优先级相同的话, 任务执行顺序是不同的(很像随机执行).
具体在concurrent队列中, 任务到底是如何分配给线程的, 我们后台会进行一个比较深的研究.
“`
以上是对GCD的比较基本的研究. 我后面会写一些GCD稍微高级一些的用法出来, 欢迎大家积极讨论.