OC多线程之GCD
要了解多线程首先要知道什么是进程,什么是进程?
#pragma mark - 串行(一个接一个,排队跑步,保持队形)队列 - (void)gcdDemo1 { // 将操作放在队列中// 使用串行队列,的异步任务非常非常非常有用!新建子线程是有开销的,不能无休止新建线程 // 创建一个串行队列 dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL); // 非ARC开发时,千万别忘记release // dispatch_release(q);for (int i = 0; i < 10; ++i) { // 异步任务,并发执行,但是如果在穿行队列中,仍然会依次顺序执行 dispatch_async(q, ^{ // [NSThread currentThread] 可以在开发中,跟踪当前线程 // num = 1,表示主线程 // num = 2,表示第2个子线程。。。 NSLog(@"%@ %d", [NSThread currentThread], i); }); } }
上面的代码是创建一个串行队列,并添加10个任务到队列中,运行这个方法时,你会看到控制台输出的顺序是从1到10的,证明这10个任务是一个执行完在执行另一个,并且是按照添加顺序来的
再来看一下并发队列里面添加10个任务会怎样
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcd2", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; ++i) { // 同步任务顺序执行 dispatch_async(q, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); }
从控制台的打印信息来看,你会看到打印是无序的,所以可以验证,并行队列不是(((按照顺序 && 执行完一个才执行一个的)))
但是前四个打印中肯定有(0,1,2,3){如果开启了四个线程的话}中的一个,因为并行队列里面前四个是0,1,2,3,所以前四个打印中,肯定会有其中一个
同时我们注意到上面向队列里面添加任务时候都是用的dispatch_async异步执行,那如果用dispatch_sync同步执行会是什么结果呢?
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcd2", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; ++i) { // 同步任务顺序执行 dispatch_sync(q, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); }
根据控制台的输出我们可以看到,输出是按顺序来的,并且打印出来的Tread信息都是主线程,证明上面任务的执行都是在主线程上完成的
(因为所以的任务都按顺序从队列里面出来,并且处理的线程就一个,所以会挨个执行)
因为这个代码所在的线程就是主线程,所以sync就在本线程中执行(不开辟新线程)
同样的,我们来看一下串行队列里的同步任务执行结果:
dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; ++i) { // 同步任务顺序执行 dispatch_sync(q, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); }
你会看到跟上面一样的结果
下面我们再来看一下并行队列时的一些问题
- (void)gcdDemo2 { dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcd2", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; ++i) { // 异步任务(第一批任务) dispatch_async(q, ^{ // [NSThread currentThread] 可以在开发中,跟踪当前线程 // num = 1,表示主线程 // num = 2,表示第2个子线程。。。 NSLog(@"%@ %d", [NSThread currentThread], i); }); } for (int i = 0; i < 10; ++i) { // 同步任务顺序执行(第二批任务) dispatch_sync(q, ^{ NSLog(@"%@ %dkkkkkk", [NSThread currentThread], i); }); } }
你会看到第一批任务里面的打印会无序的打印出来,第二批任务会按照顺序打印(带有kkkk的),但是第一批跟第二批任务是穿插进行的有时候打印第一批里面的,有时候打印第二批里面的
并且已还回发现打印第一批任务的有好几个线程(线程2,线程3...),,打印第二批任务的只有主线程
所以从这里可以看出,CPU执行任务的时候是给各个线程轮流使用的
但是你再看看下面的代码
#pragma mark - 并行(并排跑,类似于赛跑) - (void)gcdDemo2 { // 并行队列容易出错!并行队列不能控制新建线程的数量! dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcd2", DISPATCH_QUEUE_CONCURRENT); //第一轮任务 for (int i = 0; i < 10; ++i) { // 同步任务顺序执行 dispatch_sync(q, ^{ NSLog(@"%@ %dkkkk", [NSThread currentThread], i); }); } // 第二轮任务 for (int i = 0; i < 10; ++i) { // 异步任务 dispatch_async(q, ^{ // [NSThread currentThread] 可以在开发中,跟踪当前线程 // num = 1,表示主线程 // num = 2,表示第2个子线程。。。 NSLog(@"%@ %d", [NSThread currentThread], i); }); } }
你会发现第一轮任务里面的按照1,2,3,4,5,6,7.....这样打印出来,并且第一轮任务执行完毕才会执行第二轮任务
而且第二轮任务会无顺序的打印出数字,这又为什么呢,就跟上一份代码两批任务的添加顺序呢换了一下,结果差别怎么那么大呢?
因为第一轮任务使用的同步方法,什么是同步方法呢?同步就是不会创建新的线程,当执行到那个同步任务的时候,当前的线程是谁,谁就去执行这些任务
而上面的代码中,执行到同步任务的是主线程(num = 1),所以主线程就会按照顺序去执行打印任务
而第二批任务是异步方法,异步方法会创建新的线程去执行任务,所以们的ThreadNum=2,3,4等等等,并且执行顺序是无法预估的(无序打印)
到这里你可能还会有一个疑问就是为什么两批任务不会像上一份代码那样,穿插着执行,而是先执行第一批在执行第二批呢?
这事因为第一批里面用了同步并且执行到第一批的是主线程,下面我们来详细解释一下
主线程是伴随着程序运行而运行的,只要程序不挂掉,主线程就会一直运行,线程处理任务的时候是执行完一个,在执行第二个,一个线程不可能同时执行多个任务
,而且其他线程都是由主线程去创建的,主线程就类似于一个超牛逼的人,并且他会造人技术,他需要别人帮忙的时候就会造一个人出来去帮他干活,
但是这个超牛逼人每次只能干一件事,干完一件事才能干另一件,,,,你仔细看上面的代码就会发现,第一批的10个任务是先添加到主线程里面的,第二批的任务是后来出现的并且需要新的人去执行,,,,,,单此时主线程(超牛逼的那个人)已经有10个任务了,他必须先把这10个任务干完才能去开辟一个新的线程(造人帮他干活),所以
他会先把这10个任务完成以后,他才会开辟新的线程去帮他执行另外的任务,,,,,,,,,从这里就可以很轻松的理解为什么先添加同步任务和先添加异步任务结果不同了
(先添加异步任务...上上份代码... 就是先把造人的事情分给了主线程,而后才给他分配的打印的任务),所以他造出来的人会和自己同时执行任务
因为每次创建队列都很麻烦,所以苹果给我们提供了两个可以快速获取的队列---主队列和全局队列
下面是获取代码
// 每一个应用程序都只有一个主线程 // 为什么需要在主线程上工作呢? // 在iOS开发中,所有UI的更新工作,都必须在主线程上执行! dispatch_queue_t q = dispatch_get_main_queue(); // 全局队列与并行队列的区别 // 1> 不需要创建,直接GET就能用 // 2> 两个队列的执行效果相同 // 3> 全局队列没有名称,调试时,无法确认准确队列 // 记住:在开发中永远用DISPATCH_QUEUE_PRIORITY_DEFAULT // 多线程的优先级反转!低优先级的线程阻塞了高优先级的线程! dispatch_queue_t q =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
上面两行代码分别是获取主队列和全局队列的方法
添加到主队列里的任务会放在主线程里执行,这里需要注意的是,如果你使用了主队列,一定不要使用sync同步方法,而要是用异步方法
这个事为什么呢?1.主队列是一个串行队列,会依次执行任务,执行完一个执行另一个
2.sync同步方法不会创建新的线程,会在执行到那句代码所在的线程执行
因为主线程会一直执行到程序关闭,所以主线程里面的任务是执行不完的,如果在主队列里添加同步任务,因为主线程里的任务是执行不玩的,所以你添加的那个任务永远不会被执行(这里请注意,主队列和主线程不是一回事,线程是处理任务的, 队列是给线程送任务的)
在使用主队列时应该使用sync异步方法去执行任务,这样不用等主线程执行完毕就可以执行到任务,需要注意的是在主队列里不会创建新的线程,即使使用async一步方法也不会创建新的线程
可能上面的主队列比较难理解,下面来看个好理解的
#pragma mark 同步任务的阻塞 - (void)gcdSyncDemo { dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL); // 任务1 dispatch_sync(q, ^{ NSLog(@"sync %@", [NSThread currentThread]); // 任务2,同步里面包含同步,会造成阻塞 dispatch_sync(q, ^{ // 永远也不会执行到 NSLog(@"async %@", [NSThread currentThread]); }); }); }
上面是一个任务的嵌套,因为添加顺序是任务1先添加,任务2后添加,并且是同步队列,多以任务2会等任务1完成以后再执行,但是此时任务2在任务1的里面,如果任务2没有执行完,任务1是不可能执行完的,此时就是任务1等任务2完成 任务2,等任务1完成,两个任务都等待对方完成自己才能完成,这样两个任务都不会完成
而主队列里面添加同步任务就类似上面的情况,主队列里面一个巨大的任务等待小的同步任务完成,二小的同步任务等待包含他的巨打的任务完成,造成相互等待,结果就是谁完成不了