基础概念理解
进程:进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程运行在其专用的且受保护的内存中。比如:Windows系统中常用的杀进程,就是彻底关闭一个程序;手机端的爱口袋、我要聘等APP可以看成一个进程。
线程:线程是进程的基本单位,一个进程的所有任务都在线程中执行,进程要想执行任务,必须得有一个或多个线程。程序启动时,默认会开启一条线程,这条线程称之为主线程或者UI线程。
队列:队列是任务的集合,强调的是一种静态表示。或者可以说,队列是按先进先出(FIFO)管理任务对象的数据结构;通常我们将队列分为4种:串行队列、并发队列、主队列、全局队列。
同步(sync):任务一个接着一个,前一个没有执行完,后面不能执行,没有创建新线程的能力。
异步(async):开启多个新线程,任务同一时间可以一起执行。异步有开启新线程的能力,但是不一定会开启新线程(在主线程上就不会开新线程)。严格来说,异步才有多线程的概念,是多线程的代名词。
多线程的意义
- 优点:
1、能适当提高程序的执行效率
2、能适当提高资源利用率(CPU、内存)
3、线程上的任务执行完成后,线程会自动销毁 - 缺点:
1、开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512KB)
2、如果开启大量线程,会占用大量的内存空间,降低程序的性能
3、线程越多,CPU在线程调度上的开销就越大
4、程序设计更加复杂,比如线程间的通信、多线程的数据共享(比如2个人买同一张票)
iOS四种队列
- 串行队列:
dispatch_queue_create("queue.name", DISPATCH_QUEUE_SERIAL);
所有任务按顺序依次执行,结束顺序固定,符合先进先出的基本原则,队列后面的任务必须等待前面的任务执行完毕后才出队列。但是,不要认为串行队列中的所有任务都在同一个线程中执行,串行队列中的异步任务,可能会开启新线程去执行。
- 并发队列
dispatch_queue_create("queue.name", DISPATCH_QUEUE_CONCURRENT);
所有任务可以同时执行,结束顺序不固定,只要有可用线程,则队列头部任务将持续出队列。
- 主队列:
dispatch_get_main_queue()
本质是一个特殊的串行队列,主队列的任务都在主线程来执行,专门负责调度主线程度的任务,无法开辟新的线程。所以,在主队列下的任务不管是异步任务还是同步任务都不会开辟线程,任务只会在主线程顺序执行。如果在主线程上已经有任务正在执行,主队列会等到主线程空闲后再调度任务。
- 全局队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
本质是一个特殊的并发队列。在后面加入了“服务质量”和“调度优先级” 两个参数
线程与队列的组合分析
- 串行队列+异步:顺序执行,先进先出,可能会开启新线程
//串行队列
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//异步任务
for (int i=0; i<5; i++) {
dispatch_async(serial, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
NSLog(@"=============== 所有任务执行完毕 =====");
执行结果:
2022-05-17 15:27:35.976433+0800 myDemo[278:9179] =============== 所有任务执行完毕 =====
2022-05-17 15:27:35.976538+0800 myDemo[278:9420] 这是第 0 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:35.976655+0800 myDemo[278:9420] 这是第 1 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:37.978072+0800 myDemo[278:9420] 这是第 2 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:37.978150+0800 myDemo[278:9420] 这是第 3 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:39.987439+0800 myDemo[278:9420] 这是第 4 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
- 串行队列+同步:顺序执行,先进先出,不会开启新线程
//串行队列
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//同步任务
for (int i=0; i<5; i++) {
dispatch_sync(serial, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
执行结果:
2022-05-17 15:14:04.059889+0800 myDemo[252:4951] 这是第 0 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:04.059996+0800 myDemo[252:4951] 这是第 1 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:06.060559+0800 myDemo[252:4951] 这是第 2 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:06.060716+0800 myDemo[252:4951] 这是第 3 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:08.061826+0800 myDemo[252:4951] 这是第 4 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
- 并发队列+异步:同时执行,每个任务的结束顺序不确定,会开启新线程
//并发队列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//异步任务
for (int i=0; i<5; i++) {
dispatch_async(comp, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
NSLog(@"=============== 所有任务执行完毕 =====");
执行结果:
2022-05-17 15:39:06.309859+0800 myDemo[294:11951] =============== 所有任务执行完毕 =====
2022-05-17 15:39:06.309962+0800 myDemo[294:12131] 这是第 0 个任务;线程 <NSThread: 0x281719380>{number = 5, name = (null)}
2022-05-17 15:39:06.310064+0800 myDemo[294:12131] 这是第 1 个任务;线程 <NSThread: 0x281719380>{number = 5, name = (null)}
2022-05-17 15:39:06.310165+0800 myDemo[294:12134] 这是第 2 个任务;线程 <NSThread: 0x2817d2300>{number = 4, name = (null)}
2022-05-17 15:39:06.310236+0800 myDemo[294:12134] 这是第 3 个任务;线程 <NSThread: 0x2817d2300>{number = 4, name = (null)}
2022-05-17 15:39:06.311765+0800 myDemo[294:12130] 这是第 4 个任务;线程 <NSThread: 0x281719d40>{number = 6, name = (null)}
- 并发队列+同步:顺序执行,先进先出,不会开启新线程
//并发队列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//同步任务
for (int i=0; i<5; i++) {
dispatch_sync(comp, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
NSLog(@"=============== 所有任务执行完毕 =====");
执行结果:
2022-05-17 15:37:08.804872+0800 myDemo[287:11040] 这是第 0 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:08.804962+0800 myDemo[287:11040] 这是第 1 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:10.805818+0800 myDemo[287:11040] 这是第 2 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:10.806122+0800 myDemo[287:11040] 这是第 3 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:12.807415+0800 myDemo[287:11040] 这是第 4 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:12.807771+0800 myDemo[287:11040] =============== 所有任务执行完毕 =====
- 主队列+异步:不会立即执行,而是等待主队列中的所有其他除我们添加到主队列的任务都执行完毕之后,才会执行我们添加的任务
- 主队列+同步:会相互等待导致死锁
//同步任务+主线程 == 相互等待,死锁
for (int i=0; i<5; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
iOS的四种代码实现方式
pthread
C语言通用的多线程API,跨平台,程序员手动管理线程生命周期,使用难度大
NSThread
一个 NSThread 对象就代表一条线程,使用较少
//先创建再启动线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"jack"];
[thread start];
//直接创建并启动线程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"jack"];
//直接创建并启动线程
[self performSelectorInBackground:@selector(run:) withObject:@"jack"];
//使线程进入阻塞状态
[NSThread sleepForTimeInterval:2.0];
GCD
GCD 是苹果公司为多核的并行运算提出的解决方案, GCD会自动利用更多的 CPU 内核(比如双核、四核)来开启线程执行任务,GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程),不需要我们程序员手动管理内存。
补充一个栅栏的示例代码:
//并发队列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//异步任务
for (int i=0; i<5; i++) {
dispatch_async(comp, ^{
NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
if (i==1 || i==3) {
[NSThread sleepForTimeInterval:2];
}
});
}
dispatch_barrier_async(comp, ^{
NSLog(@"这是第 dispatch_barrier_async 个任务;线程 %@",[NSThread currentThread]);
});
NSLog(@"=============== 所有任务执行完毕 =====");
执行结果:
2022-05-17 16:17:57.471306+0800 myDemo[321:16764] =============== 所有任务执行完毕 =====
2022-05-17 16:17:57.471536+0800 myDemo[321:16980] 这是第 1 个任务;线程 <NSThread: 0x2825a7600>{number = 6, name = (null)}
2022-05-17 16:17:57.471645+0800 myDemo[321:16985] 这是第 0 个任务;线程 <NSThread: 0x2825b83c0>{number = 5, name = (null)}
2022-05-17 16:17:57.471751+0800 myDemo[321:16985] 这是第 2 个任务;线程 <NSThread: 0x2825b83c0>{number = 5, name = (null)}
2022-05-17 16:17:57.471894+0800 myDemo[321:16985] 这是第 3 个任务;线程 <NSThread: 0x2825b83c0>{number = 5, name = (null)}
2022-05-17 16:17:57.472199+0800 myDemo[321:16979] 这是第 4 个任务;线程 <NSThread: 0x282546580>{number = 3, name = (null)}
2022-05-17 16:17:59.495622+0800 myDemo[321:16985] 这是第 dispatch_barrier_async 个任务;线程 <NSThread: 0x2825b83c0>{number = 5, name = (null)}
NSOperation、NSOperationQueue
NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。
//创建操作方法,启动
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationLoad:) object:@{@"obj":@"loadContent"}];
[invocationOperation start];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
[self operationLoad:@{@"operation":@"11111111111"}];
}];
[operation start];
NSLog(@"invocationOperation/operation 开启start命令");
//创建队列
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operationTwo = [NSBlockOperation blockOperationWithBlock:^{
[self operationLoad:@{@"operation":@"2222222222"}];
}];
[opQueue addOperation:operationTwo];
//队列直接添加block方法
[opQueue addOperationWithBlock:^{
[self operationLoad:@{@"addOperationWithBlock":@"333333333"}];
}];
NSBlockOperation *operationFive = [NSBlockOperation blockOperationWithBlock:^{
[self operationLoad:@{@"operation":@"5555555555"}];
}];
//队列添加任务组
[opQueue addOperations:@[operationFive] waitUntilFinished:NO];
//添加栅栏功能
if (@available(iOS 13.0, *)) {
[opQueue addBarrierBlock:^{
[self operationLoad:@{@"addBarrierBlock":@"888888888"}];
}];
} else {
// Fallback on earlier versions
}
NSLog(@"=============== 所有任务执行完毕 =====");
执行结果:
2022-05-17 16:54:34.193616+0800 myDemo[356:25036] 传递参数== {
obj = loadContent;
} ,线程== <NSThread: 0x28147a9c0>{number = 1, name = main}
2022-05-17 16:54:36.196363+0800 myDemo[356:25036] 传递参数== {
operation = 11111111111;
} ,线程== <NSThread: 0x28147a9c0>{number = 1, name = main}
2022-05-17 16:54:36.196762+0800 myDemo[356:25036] invocationOperation/operation 开启start命令
2022-05-17 16:54:36.197310+0800 myDemo[356:25036] =============== 所有任务执行完毕 =====
2022-05-17 16:54:38.321310+0800 myDemo[356:25232] 传递参数== {
operation = 5555555555;
} ,线程== <NSThread: 0x281424e40>{number = 4, name = (null)}
2022-05-17 16:54:38.322240+0800 myDemo[356:25237] 传递参数== {
addOperationWithBlock = 333333333;
} ,线程== <NSThread: 0x2814259c0>{number = 3, name = (null)}
2022-05-17 16:54:38.322340+0800 myDemo[356:25231] 传递参数== {
operation = 2222222222;
} ,线程== <NSThread: 0x281824740>{number = 14, name = (null)}