1.多线程知识:
线程与队列的区别:线程是代码执行的路径,队列则是用于保存以及管理任务的,线程负责去队列中取任务进行执行。
例子:
-(void)task{
NSLog(@"1. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread],dispatch_get_current_queue());
}
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
[self task];
}); //输出 {number = 1, name = main}, 当前队列是: net.bujige.testQueue
[self task]; //输出:{number = 1, name = main}, 当前队列是: com.apple.main-thread
可以看到两次代码分别在queue和main queue里面但是都被主线程执行
上面可以看出主线程可以执行非主队列的任务,那么主队列的任务可以由子线程执行吗?请看代码!
dispatch_async(dispatch_get_main_queue(), ^{
[self task];
}); //输出{number = 1, name = main},当前队列是:com.apple.main-thread
2.GCD的用法
Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。
GCD 可用于多核的并行运算
GCD 会自动利用更多的 CPU 内核(比如双核、四核)
GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码
1> iOS和OS X的核心是XNU内核,GCD是基于XNU内核实现的2> GCD的API全部在libdispatch库中
3> GCD的底层实现主要有Dispatch Queue和Dispatch Source
• Dispatch Queue :管理block(操作)
• Dispatch Source :处理事件
常规的方法就不列举了,下面是一些稍微不太常用的
基础用法:
1. 任务和队列
GCD中有2个核心概念
任务(block):执行的具体操作
队列(queue):用来调度任务
2. 同步和异步的区别
同步:在当前线程中执行
异步:在另一条线程中执行
dispatch_get_main_queue():主队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0):全局并发队列
高级用法:
1.dispatch_barrier_async 屏障
我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于 栅栏 一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。
dispatch_barrier_async和dispatch_barrier_sync的区别:barrier_async只会将本队列中的任务设置障碍而不会阻碍其它队列的代码.
如:- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 追加任务1
for(inti = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for(inti = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_barrier_async(queue, ^{
// 追加任务 barrier
for(inti = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务3
for(inti = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务4
for(inti = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"4---%@",[NSThread currentThread]); // 打印当前线程
}
});
}
输出结果:
2018-02-23 20:48:18.297745+0800 YSC-GCD-demo[20188:5059274] 1---{number = 4, name = (null)}
2018-02-23 20:48:18.297745+0800 YSC-GCD-demo[20188:5059273] 2---{number = 3, name = (null)}
2018-02-23 20:48:20.301139+0800 YSC-GCD-demo[20188:5059274] 1---{number = 4, name = (null)}
2018-02-23 20:48:20.301139+0800 YSC-GCD-demo[20188:5059273] 2---{number = 3, name = (null)}
2018-02-23 20:48:22.306290+0800 YSC-GCD-demo[20188:5059274] barrier---{number = 4, name = (null)}
2018-02-23 20:48:24.311655+0800 YSC-GCD-demo[20188:5059274] barrier---{number = 4, name = (null)}
2018-02-23 20:48:26.316943+0800 YSC-GCD-demo[20188:5059273] 4---{number = 3, name = (null)}
2018-02-23 20:48:26.316956+0800 YSC-GCD-demo[20188:5059274] 3---{number = 4, name = (null)}
2018-02-23 20:48:28.320660+0800 YSC-GCD-demo[20188:5059273] 4---{number = 3, name = (null)}
2018-02-23 20:48:28.320649+0800 YSC-GCD-demo[20188:5059274] 3---{number = 4, name = (null)}
2.dispatch_once只执行一次
3. GCD 快速迭代方法:dispatch_apply
dispatch_apply类似一个for循环,会在指定的dispatch queue中运行block任务n次,如果队列是并发队列,则会并发执行block任务,dispatch_apply是一个同步调用,block任务执行n次后才返回,才能继续往下执行
并发队列
dispatch_queue_tqueue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT); dispatch_apply(5, queue, ^(size_t i) {
NSLog(@"%@我开始执行 %zu times",[NSThreadcurrentThread],i+1);
});
NSLog(@"done");
输出结果:
[3545:1502823] {number = 1, name = main}我开始执行 1 times
[3545:1502839] {number = 2, name = (null)}我开始执行 2 times
[3545:1502839] {number = 2, name = (null)}我开始执行 4 times
[3545:1502839] {number = 2, name = (null)}我开始执行 5 times
[3545:1502823] {number = 1, name = main}我开始执行 3 times
[3553:1504850] done
串行队列:
dispatch_queue_tqueue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); dispatch_apply(5, queue, ^(size_t i) {
NSLog(@"%@我开始执行 %zu times",[NSThreadcurrentThread],i+1);
});
NSLog(@"done");
输出结果:
[3535:1501629] {number = 1, name = main}我开始执行 1 times
[3535:1501629] {number = 1, name = main}我开始执行 2 times
[3535:1501629] {number = 1, name = main}我开始执行 3 times
[3535:1501629] {number = 1, name = main}我开始执行 4 times
[3535:1501629] {number = 1, name = main}我开始执行 5 times
[3553:1504850] done
4.GCD 的队列组:dispatch_group
用于异步执行两个耗时任务,然后2个耗时任务完毕后再执行另一个任务
- (void)groupNotify {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---begin");
dispatch_group_tgroup = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务1
for(inti =0; i <2; ++i) {
[NSThreadsleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务2
for(inti =0; i <2; ++i) {
[NSThreadsleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
for(inti =0; i <2; ++i) {
[NSThreadsleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
NSLog(@"group---end");
});
NSLog(@"4");
}
打印为:currentThread---{number = 1, name = main}
group---begin
4
2---{number = 3, name = (null)}
1---{number = 4, name = (null)}
1---{number = 4, name = (null)}
2---{number = 3, name = (null)}
3---{number = 1, name = main}
3---{number = 1, name = main}
group---end
注意1:可以看到任务1和任务2执行完之后,notify里面的内容才执行;而 NSLog(@"4");在任务一和任务二之前执行,如果我们想所有任务都执行之后再执行NSLog(@"4")该怎样做?
在NSLog(@"4")之前使用dispatch_group_wait(group,DISPATCH_TIME_FOREVER);即可阻塞线程直到任务一和任务二执行完成
注意2:dispatch_group_async里面如果有异步操作,会导致其block里面的任务还没执行完,dispatch_group_notify就执行了;下面用dispatch_group_enter解决这个问题
4.2用dispatch_group_enter、dispatch_group_leave代替dispatch_group_async
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任务1
for(inti = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任务2
for(inti = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程.
for(inti = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
NSLog(@"group---end");
});
输出结果:
2018-02-23 22:14:17.997667+0800 YSC-GCD-demo[20592:5214830] currentThread---{number = 1, name = main}
2018-02-23 22:14:17.997839+0800 YSC-GCD-demo[20592:5214830] group---begin
2018-02-23 22:14:20.000298+0800 YSC-GCD-demo[20592:5215094] 1---{number = 4, name = (null)}
2018-02-23 22:14:20.000305+0800 YSC-GCD-demo[20592:5215095] 2---{number = 3, name = (null)}
2018-02-23 22:14:22.001323+0800 YSC-GCD-demo[20592:5215094] 1---{number = 4, name = (null)}
2018-02-23 22:14:22.001339+0800 YSC-GCD-demo[20592:5215095] 2---{number = 3, name = (null)}
2018-02-23 22:14:24.002321+0800 YSC-GCD-demo[20592:5214830] 3---{number = 1, name = main}
2018-02-23 22:14:26.002852+0800 YSC-GCD-demo[20592:5214830] 3---{number = 1, name = main}
2018-02-23 22:14:26.003116+0800 YSC-GCD-demo[20592:5214830] group---end
5.GCD 信号量:dispatch_semaphore
dispatch_semaphore_create(long value) 创建dispatch_semaphore_t类型的信号量,vaule为初始信号量,注意value必须大于等于0否则返回null
dispatch_semaphore_signal:让信号总量加1 至于返回值接下来再讲
dispatch_semaphore_wait(dispatch_semaphore_t dsema,dispatch_time_t timeout):可以使总信号量减1,这个函数的作用为:如果dsema信号量大于0,则继续执行代码且将信号量的值减1;如果desema的值为0,那么就会阻塞,直到timeout;如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,那么就继续向下执行并将信号量减1
信号量可以用车位来表示:
停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。信号量的值就相当于剩余车位的数目,dispatch_semaphore_create(long value)指明了初始停车位的剩余数目,dispatch_semaphore_signal就相当于走了一辆车,dispatch_semaphore_wait函数就相当于来了一辆车。当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待
Dispatch Semaphore 在实际开发中主要用于:
保持线程同步,将异步执行任务转换为同步执行任务
保证线程安全,为线程加锁
异步转化为同步:
- (void)semaphoreSync {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); //信号量为1
__block intnumber = 0;
dispatch_async(queue, ^{
// 追加任务1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
number = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,number = %zd",number);
}
线程安全:
/**
* 线程安全:使用 semaphore 加锁
* 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
*/
- (void)initTicketStatusSave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"semaphore---begin");
semaphoreLock = dispatch_semaphore_create(1); //信号量为2
self.ticketSurplusCount = 50;
// queue1 代表北京火车票售卖窗口
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 代表上海火车票售卖窗口
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketSafe];
});
}
/**
* 售卖火车票(线程安全)
*/
- (void)saleTicketSafe {
while(1) {
// 相当于加锁
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if(self.ticketSurplusCount > 0) { //如果还有票,继续售卖
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else{ //如果已卖完,关闭售票窗口
NSLog(@"所有火车票均已售完");
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
break;
}
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
}
}
6.dispatch_ones
只会执行一次,且是线程安全的
*7.timer
__block int timeout = 30; //倒计时时间
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), 1*NSEC_PER_SEC, 2ull*NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
if(timeout ==0)//倒计时结束,关闭
{
NSLog(@"结束");
dispatch_source_cancel(timer); //没有dispatch_source_cancel()方法,timer不会执行
// dispatch_suspend(timer);
}
else
{
NSLog(@" == %@, == %@",[NSString stringWithFormat:@"%.2d", timeout--],[NSDate new]);
}
});
dispatch_source_set_cancel_handler(timer, ^{
NSLog(@"cancel");
});
//启动
dispatch_resume(timer);