多线程中的队列有:
串行队列,并发队列,全局队列(并发),主队列(串行)。
执行的方法有:同步执行和异步执行。
提到多线程:pthread,NSThread,GCD,NSOperation
其中phtread是跨平台的。
GCD和NSOperation都是常用的,后者是基于前者的。
两者区别:
GCD的核心概念是将一个任务添加到队列,指定任务执行的方法,然后执行。
NSOperation则是直接将一个操作添加到队列中。
1.串行队列,同步执行
/创建串行队列
dispatch_queue_t testqueue = dispatch_queue_create("queue2017", NULL);
// 执行任务
for (int i = 0; i<10; i++)
{
dispatch_sync(testqueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"串行队列 同步执行");
运行结果:
执行结果可以看到全在主线程执行,并且是按照数序执行,循环结束之后主线程的打印才输出。
2.串行队列,异步执行
//创建串行队列
dispatch_queue_t testqueue = dispatch_queue_create("queue2017", NULL);
// 执行任务
for (int i = 0; i<10; i++)
{
dispatch_async(testqueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"串行队列 异步执行");
运行结果:
结果显示,系统开了1条异步线程,因此全部在线程3执行,并且是顺序执行。主线程打印虽然在最上面,但是这个先后顺序是不确定,有可能会混在中间。
3.并发队列,同步执行
//创建并发队列
dispatch_queue_t testqueue = dispatch_queue_create("queue2017", DISPATCH_QUEUE_CONCURRENT);
// 执行任务
for (int i = 0; i<10; i++)
{
dispatch_sync(testqueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"并发队列 同步执行");
运行结果:
这个运行结果和第1种的(串行队列,同步执行)是一模一样的。
因为同步任务的概念就是按顺序执行,后面都要等。言外之意就是不允许多开线程。所以一旦是同步执行,前面什么队列已经没区别了。
4.并发队列,异步执行
//创建并发队列
dispatch_queue_t testqueue = dispatch_queue_create("queue2017", DISPATCH_QUEUE_CONCURRENT);
// 执行任务
for (int i = 0; i<10; i++)
{
dispatch_async(testqueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"并发队列 异步执行");
系统开了多条线程,并且执行的顺序也是乱序的
5.主队列,同步执行
NSLog(@"之前 - %@", [NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync - %@", [NSThread currentThread]);
});
NSLog(@"之后 - %@", [NSThread currentThread]);
运行结果为卡死
卡死的原因是循环等待,主队列的东西要等主线程执行完,而因为是同步执行不能开线程,所以下面的任务要等上面的任务执行完,所以卡死。
6.主队列,异步执行
// 主队列 - 程序启动之后已经存在主线程,主队列同样存在
dispatch_queue_t q = dispatch_get_main_queue();
// 安排一个任务
for (int i = 0; i<10; i++)
{
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"sleep");
[NSThread sleepForTimeInterval:2.0];
NSLog(@"主队列,异步执行");
主线程在睡会之后才打印,循环一直在等着。因为主队列的任务虽然会加到主线程中执行,但是如果主线程里也有任务就必须等主线程任务执行完才轮到主队列的。
7.同步任务的使用
dispatch_queue_t q = dispatch_queue_create("queue2017", DISPATCH_QUEUE_CONCURRENT);
// 1. 用户登录,必须要第一个执行
dispatch_sync(q, ^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"用户登录 %@", [NSThread currentThread]);
});
// 2. 扣费
dispatch_async(q, ^{
NSLog(@"扣费 %@", [NSThread currentThread]);
});
// 3. 下载
dispatch_async(q, ^{
NSLog(@"下载 %@", [NSThread currentThread]);
});
NSLog(@"同步任务的使用");
“用户登陆”在主线程打印,后两个在异步线程打印。
上面的“用户登陆”使用同步执行,后面的扣费和下载都是异步执行。所以“用户登陆”必须第一个打印出来不管等多久,然后后面的两个异步和主线程打印会不确定顺序的打印。这就是日常开发中,那些后面对其有依赖的必须要先执行的任务使用同步执行,然后反正都要执行先后顺序无所谓的使用异步执行。
8.block异步任务包裹同步任务
dispatch_queue_t queue = dispatch_queue_create("queue2017", DISPATCH_QUEUE_CONCURRENT);
void (^task)() = ^ {
// 1. 用户登录,必须要第一个执行
dispatch_sync(queue, ^{
NSLog(@"用户登录 %@", [NSThread currentThread]);
});
// 2. 扣费
dispatch_async(queue, ^{
NSLog(@"扣费 %@", [NSThread currentThread]);
});
// 3. 下载
dispatch_async(queue, ^{
NSLog(@"下载 %@", [NSThread currentThread]);
});
};
dispatch_async(queue, task);
[NSThread sleepForTimeInterval:1.0];
NSLog(@"block异步任务包裹同步任务");
因为整个block是在异步执行的,所以即使里面“用户登陆”是同步执行,那也无法在主线程中执行,只能开一条异步线程执行,因为是同步的所以必须等他先执行,后面的“扣费”和“下载”在上面同步执行结束之后,不确定顺序的打印。
9.全局队列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++)
{
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
[NSThread sleepForTimeInterval:1.0];
NSLog(@"全局队列");
全局队列的本质就是并发队列,只是在后面加入了,“服务质量”,和“调度优先级” 两个参数,这两个参数一般为了系统间的适配,最好直接填0和0。
10.总之
- 开不开线程:同步不开,异步开。
- 开几条线程:串行队列开一条,并发开多条(异步)
- 主队列:专门用来在主线程上调度任务的"队列",主队列不能在其他线程中调度任务!
- 如果主线程上当前正在有执行的任务,主队列暂时不会调度任务的执行!主队列同步任务,会造成死锁。原因是循环等待
- 同步任务可以队列调度多个异步任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这是依赖关系。
- 全局队列:并发,能够调度多个线程,执行效率高,但是相对费电。 串行队列效率较低,省电省流量,或者是任务之间需要依赖也可以使用串行队列。
- 也可以通过判断当前用户的网络环境来决定开的线程数。WIFI下6条,3G/4G下2~3条。