@qidiandasheng
2022-08-07T21:46:39.000000Z
字数 4623
阅读 4458
iOS实战
进程
正在运行的程序被称为进程,负责程序运行的内存分配,每一个进程都有独立的虚拟内存空间
线程
线程是进程中最小的执行单元,一个进程可以有多条线程,但是至少包含一条线程,被称为主线程。线程里有非常多的任务。
1.同一时间单核CPU只能执行一个线程,多线程是CPU快速在多个线程之间切换,造成多个线程同时执行的假象
2.多核CPU是真的同时处理多个线程
并发(concurrency)
在一条线程上通过快速切换,让人感觉在同步进行
并行(parallelism)
充分利用计算机的多核,在多个线程上同步进行
并发和并行的区别
并行是真正的异步,多核CPU可以同时开启多条线程供多个任务同时执行,互不干扰。但是并发就不一样了,是一个伪异步。在单核CUP中只能有一条线程,但是又想执行多个任务。这个时候,只能在一条线程上不停的切换任务,比如任务A执行了20%,任务A停下里,线程让给任务B,任务执行了30%停下,再让任务A执行。这样我们用的时候,由于CUP处理速度快,你看起来好像是同时执行,其实不是的,同一时间只会执行单个任务。
注:在多核CPU里我们创建并发队列异步执行时有可能是真正的并行。
在 GCD 中,加入了两个非常重要的概念: 任务 和 队列。
任务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行 和 异步执行。
同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!
如果是 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。
如果是 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。一般会开其他线程来运行任务。
队列:用于存放任务。一共有两种队列, 串行队列 和 并发队列。
放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,然后取下一个,这样一个一个的执行。
并发队列中的任务根据同步或异步有不同的执行方式。
放到并发队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。
同步方式 | 全局并发队列 | 手动创建的串行队列 | 主队列 |
---|---|---|---|
同步(sync) | 没有开启新的线程;串行执行任务 | 没有开启新的线程;串行执行任务 | 没有开启新的线程;串行执行任务 |
异步(async) | 有开启新的线程;并行执行任务 | 有开启新的线程;串行执行任务 | 没有开启新的线程;串行执行任务 |
不会创建新线程,任务一个一个按顺序执行。
dispatch_queue_t serialQueue = dispatch_queue_create("serial_queue",
DISPATCH_QUEUE_SERIAL);
for (int i = 0; i<10; i++) {
dispatch_sync(serialQueue, ^{
sleep(1);
NSLog(@"%@ 执行任务%d current theard:%@",[NSDate date],i,[NSThread currentThread]);
});
}
不用等待一个任务执行完毕才开始调度下一个任务,但是同步任务不会开启新的线程,所以也是一个一个的执行。
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent_queue",
DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<10; i++) {
dispatch_sync(concurrentQueue, ^{
sleep(1);
NSLog(@"%@ 执行任务%d current theard:%@",[NSDate date],i,[NSThread currentThread]);
});
}
可以看到,程序没有并发执行,而且没有开启新线程,是在主线程执行的。 有没有觉得奇怪呢?为什么向并发队列添加的任务,没有开启新线程,而是在主线程执行的? 如下解释:
使用dispatch_sync 添加同步任务,必须等添加的block执行完成之后才返回。
既然要执行block,肯定需要线程,要么新开线程执行,要么再已存在的线程(包括当前线程)执行。
dispatch_sync的官方注释里面有这么一句话:
As an optimization, dispatch_sync() invokes the block on the current thread when possible.
作为优化,如果可能,直接在当前线程调用这个block。
所以一般在大多数情况下,通过dispatch_sync添加的任务,在哪个线程添加就会在哪个线程执行。
上面我们添加的任务的代码是在主线程,所以就直接在主线程执行了。
按顺序来调度任务,任务会创建一个新的线程来执行。
dispatch_queue_t serialQueue = dispatch_queue_create("serial_queue",
DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<10; i++) {
dispatch_async(serialQueue, ^{
sleep(1);
NSLog(@"%@ 执行任务%d current theard:%@",[NSDate date],i,[NSThread currentThread]);
});
}
使用
dispatch_async
函数添加到serial dispatch queue
中的任务,一般会(不一定)新开一个线程,但是不同的异步任务用的是同一个线程。
会创建多个线程,操作并发无序执行。
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent_queue",
DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<10; i++) {
dispatch_async(concurrentQueue, ^{
sleep(1);
NSLog(@"%@ 执行任务%d current theard:%@",[NSDate date],i,[NSThread currentThread]);
});
}
操作不会新建线程,操作顺序执行。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i<10; i++) {
dispatch_sync(globalQueue, ^{
sleep(1);
NSLog(@"%@ 执行任务%d current theard:%@",[NSDate date],i,[NSThread currentThread]);
});
}
创建多个线程,操作无序执行。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i<10; i++) {
dispatch_async(globalQueue, ^{
sleep(1);
NSLog(@"%@ 执行任务%d current theard:%@",[NSDate date],i,[NSThread currentThread]);
});
}
主线程不会结束,主队列中的同步操作永远不会被执行,会死锁。
viewDidLoad
算是一个完整的任务,而block里面算第二个任务,主队列的顺序是[viewDidLoad
、block
]。所以block
要等viewDidLoad
执行完才能执行,但是viewDidLoad
里又有一个同步任务block
需要执行完才能执行下去。这样就造成了互相等待,最终形成死锁。
- (void)viewDidLoad {
NSLog(@"任务1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"%@ 执行任务2 current theard:%@",[NSDate date],[NSThread currentThread]);
});
NSLog(@"任务1");
}
- (void)viewDidLoad {
NSLog(@"任务1");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@ 执行任务2 current theard:%@",[NSDate date],[NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@ 执行任务3 current theard:%@",[NSDate date],[NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@ 执行任务4 current theard:%@",[NSDate date],[NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@ 执行任务5 current theard:%@",[NSDate date],[NSThread currentThread]);
});
NSLog(@"任务6");
}
test[96860:10845080] 任务1
test[96860:10845080] 任务6
test[96860:10845080] 2021-03-18 03:24:05 +0000 执行任务2 current theard:<NSThread: 0x600000fe0180>{number = 1, name = main}
test[96860:10845080] 2021-03-18 03:24:05 +0000 执行任务3 current theard:<NSThread: 0x600000fe0180>{number = 1, name = main}
test[96860:10845080] 2021-03-18 03:24:05 +0000 执行任务4 current theard:<NSThread: 0x600000fe0180>{number = 1, name = main}
test[96860:10845080] 2021-03-18 03:24:05 +0000 执行任务5 current theard:<NSThread: 0x600000fe0180>{number = 1, name = main}