闲谈
本文主要总结概括一下实际工作中多线程,以及自己对多线程的理解
多线程使用方式
- Thread
- NSOperation
- GCD
- 、、、 本文主要总结GCD的一些使用方法。
GCD简介
- Grand Central Dispatch(GCD)是异步执行任务的技术之一。讲用用程序中记述的线程管理使用代码在系统中实现。由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样就更有效率。
- GCD主要有任务和队列构成
GCD中的概念
概念 | API | 理解 |
---|---|---|
队列 | Dispatch Queue | 线程队列,类似于任务池,将定义好的任务添加进入队列即可按照一定规则执行(并发Concurrent、串行Serial) |
异步线程 | dispatch_async | |
同步线程 | dispatch_sync | 将任务追加到指定的队列中,追加的Block结束之前,dispatch_sync函数会一直等待。 |
栅栏函数 | diapatch_barrier_async | 将任务追加到指定的队列中,dispatch_sync函数不会做任何等待。 |
一次性函数 | dispatch_once | 添加的Block任务在整个程序生命周期中仅仅执行一次 |
线程组 | Dispatch Group | |
信号量 | Dispatch Semaphone |
DCG使用方法
- 1.创建一个队列(串行队列、并发队列)
- 2.创建任务,将任务追加到队列中,系统根据任务类型执行任务(同步、异步)
队列创建
- 串行队列(Serial Dispatch Queue)
- 每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
- 并发队列(Concurrent Dispatch Queue)
- 可以让多个任务并发执行。可以开启多个线程,同时执行多个任务
//串行队列
dispatch_queue_t queue = dispatch_queue_create("com.queue.serial", DISPATCH_QUEUE_SERIAL);
//并发队列
dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
任务创建
- 异步
dispatch_async(queue, ^{
// 异步任务
});
- 同步
dispatch_sync(queue, ^{
// 同步任务
});
由于有两种任务执行方式,以及两种队列方式,所以会有多种组合方式;
任务和队列的组合方式
并发队列 | 串行队列 | 主队列 | |
---|---|---|---|
同步(sync) | 不开启新线程,串行执行任务 | 不开启新线程,串行执行任务 | 死锁,任务等待主线程执行,但是是同步,主线程等待任务执行,造成死锁 |
异步(async) | 开启新线程,并发执行任务 | 开启一条新线程,串行执行任务 | 不开启新线程,串行执行任务 |
GCD具体实现
- 串行队列+异步任务
dispatch_queue_t queue = dispatch_queue_create("com.queue.serial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3--%@",[NSThread currentThread]);
});
NSLog(@"任务4--%@",[NSThread currentThread]);
输出结果:
objc[1491:2645283] 任务4--<NSThread: 0x281490dc0>{number = 1, name = main}
objc[1491:2645294] 任务1--<NSThread: 0x2814c5680>{number = 5, name = (null)}
objc[1491:2645294] 任务2--<NSThread: 0x2814c5680>{number = 5, name = (null)}
objc[1491:2645294] 任务3--<NSThread: 0x2814c5680>{number = 5, name = (null)}
- 串行队列+同步任务
dispatch_queue_t queue = dispatch_queue_create("com.queue.serial", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3--%@",[NSThread currentThread]);
});
NSLog(@"任务4--%@",[NSThread currentThread]);
输出结果:
objc[1489:2644861] 任务1--<NSThread: 0x280c28dc0>{number = 1, name = main}
objc[1489:2644861] 任务2--<NSThread: 0x280c28dc0>{number = 1, name = main}
objc[1489:2644861] 任务3--<NSThread: 0x280c28dc0>{number = 1, name = main}
objc[1489:2644861] 任务4--<NSThread: 0x280c28dc0>{number = 1, name = main}
- 并发队列+异步执行
ispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 异步串行队列
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
// 异步串行队列
NSLog(@"任务2--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
// 异步串行队列
NSLog(@"任务3--%@",[NSThread currentThread]);
});
NSLog(@"任务4--%@",[NSThread currentThread]);
输出结果:
objc[1497:2646937] 任务4--<NSThread: 0x283e80dc0>{number = 1, name = main}
objc[1497:2646950] 任务2--<NSThread: 0x283ede540>{number = 5, name = (null)}
objc[1497:2646950] 任务3--<NSThread: 0x283ede540>{number = 5, name = (null)}
objc[1497:2646949] 任务1--<NSThread: 0x283ed1e80>{number = 4, name = (null)}
- 并发队列+ 同步任务
dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3--%@",[NSThread currentThread]);
});
NSLog(@"任务4--%@",[NSThread currentThread]);
输出结果
objc[1501:2647457] 任务1--<NSThread: 0x2811d4dc0>{number = 1, name = main}
objc[1501:2647457] 任务2--<NSThread: 0x2811d4dc0>{number = 1, name = main}
objc[1501:2647457] 任务3--<NSThread: 0x2811d4dc0>{number = 1, name = main}
objc[1501:2647457] 任务4--<NSThread: 0x2811d4dc0>{number = 1, name = main}
- 主队列+异步任务
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// 异步串行队列
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
// 异步串行队列
NSLog(@"任务2--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
// 异步串行队列
NSLog(@"任务3--%@",[NSThread currentThread]);
});
NSLog(@"任务4--%@",[NSThread currentThread]);
输出结果:
objc[1504:2648185] 任务4--<NSThread: 0x283544dc0>{number = 1, name = main}
objc[1504:2648185] 任务1--<NSThread: 0x283544dc0>{number = 1, name = main}
objc[1504:2648185] 任务2--<NSThread: 0x283544dc0>{number = 1, name = main}
objc[1504:2648185] 任务3--<NSThread: 0x283544dc0>{number = 1, name = main}
- 主队列+同步任务
- 程序崩溃,由于死锁程序无法执行
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2--%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3--%@",[NSThread currentThread]);
});
NSLog(@"任务4--%@",[NSThread currentThread]);
栅栏函数
-设置栅栏拦截程序,队列中在栅栏之前的任务执行结束之前,栅栏后面的任务会一直等待
dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2--%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
for (int i = 0; i<4; i++) {
NSLog(@"任务5--%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i<4; i++) {
NSLog(@"任务3--%@",[NSThread currentThread]);
}
});
NSLog(@"任务4--%@",[NSThread currentThread]);
输出结果:
objc[1517:2651721] 任务4--<NSThread: 0x283a74dc0>{number = 1, name = main}
objc[1517:2651733] 任务1--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务2--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务5--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务5--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务5--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务5--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务3--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务3--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务3--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务3--<NSThread: 0x283a22880>{number = 4, name = (null)}
一次行函数
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//任务代码
});
线程组
- 线程组在实际中主要应用场景是:需要在多个异步操作完成之后进行特定操作
- dispatch_group_notify 函数在所有 dispatch_group_async 任务执行完毕之后才会通知开始后续操作 场景示例:APP中后台加载多个图片完成之后,在进行图片合成,组合成一张新的图片
dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"任务0--%@",[NSThread currentThread]);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"任务2--%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"任务3--%@",[NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//等待前面耗时操作完成回到主线程
[NSThread sleepForTimeInterval:1];
NSLog(@"任务4--%@",[NSThread currentThread]);
});
NSLog(@"任务5--%@",[NSThread currentThread]);
输出结果
2020-11-20 11:26:45.652124+0800 objc[1527:2656709] 任务0--<NSThread: 0x2814e0dc0>{number = 1, name = main}
2020-11-20 11:26:45.652206+0800 objc[1527:2656709] 任务5--<NSThread: 0x2814e0dc0>{number = 1, name = main}
2020-11-20 11:26:46.657634+0800 objc[1527:2656718] 任务2--<NSThread: 0x2814e7ac0>{number = 3, name = (null)}
2020-11-20 11:26:46.657748+0800 objc[1527:2656719] 任务1--<NSThread: 0x2814db800>{number = 5, name = (null)}
2020-11-20 11:26:46.657767+0800 objc[1527:2656720] 任务3--<NSThread: 0x2814a8040>{number = 6, name = (null)}
2020-11-20 11:26:47.659350+0800 objc[1527:2656709] 任务4--<NSThread: 0x2814e0dc0>{number = 1, name = main}
- 线程组也可以使用 dispatch_group_enter、dispatch_group_leave完成
dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"任务0--%@",[NSThread currentThread]);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"任务1--%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"任务2--%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"任务3--%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//等待前面耗时操作完成回到主线程
[NSThread sleepForTimeInterval:1];
NSLog(@"任务4--%@",[NSThread currentThread]);
});
NSLog(@"任务5--%@",[NSThread currentThread]);
输出结果
2020-11-20 11:34:55.314133+0800 objc[1530:2658637] 任务0--<NSThread: 0x280f78dc0>{number = 1, name = main}
2020-11-20 11:34:55.314234+0800 objc[1530:2658637] 任务5--<NSThread: 0x280f78dc0>{number = 1, name = main}
2020-11-20 11:34:56.319524+0800 objc[1530:2658651] 任务2--<NSThread: 0x280f29340>{number = 4, name = (null)}
2020-11-20 11:34:56.319513+0800 objc[1530:2658649] 任务1--<NSThread: 0x280f29540>{number = 3, name = (null)}
2020-11-20 11:34:56.319536+0800 objc[1530:2658650] 任务3--<NSThread: 0x280f34b80>{number = 6, name = (null)}
2020-11-20 11:34:57.321272+0800 objc[1530:2658637] 任务4--<NSThread: 0x280f78dc0>{number = 1, name = main}
信号量
- 此函数理解为栅栏函数和串行队列的补充,更细粒度的控制
//初始化信号量
dispatch_semaphore_t semaphonre = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"操作开始");
for (int i = 1; i < 10; i ++) {
dispatch_async(queue, ^{
/*
1.判断信号量是否>0
2.只有信号量>0,后面代码才会继续执行
3.DISPATCH_TIME_FOREVER 表示会一直等待
*/
dispatch_semaphore_wait(semaphonre, DISPATCH_TIME_FOREVER);
//此处同时只有一个线程进行访问,线程安全
[NSThread sleepForTimeInterval:2];
NSLog(@"操作===%d",i);
/*
1.执行完一次操作之后信号量+1
2.信号量+1之后,原本dispatch_semaphore_wait处等待的操作按照先进先出原则进行执行
*/
dispatch_semaphore_signal(semaphonre);
});
}
NSLog(@"操作结束");
参考
- 《Objective-C 高级编程》
- juejin.cn/post/684490…