iOS开发之GCD多线程一

多线程的一些相关概念

进程和线程:操作系统学习笔记之进程与线程

同步和异步

同步任务:在执行任务的过程中,按照顺序依次执行。
异步任务:在执行任务的过程中,可以同时触发执行。

串行和并行

串行队列:队列中任务依次执行。
并行队列:队列中任务同时执行。

4种多线程操作情况

分别用同步串行、同步并行、异步串行和异步并行来执行以下函数。

1
2
3
4
5
6
-(void)task:(int)index{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务%d————当前线程:%@", index, [NSThread currentThread]);
}
}

同步执行串行任务队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(void)syncSerial{
dispatch_queue_t serialQueue = dispatch_queue_create("LJ_SERIAL_QUEUE", DISPATCH_QUEUE_SERIAL);
NSLog(@"开始同步串行任务————————当前线程:%@", [NSThread currentThread]);
dispatch_sync(serialQueue, ^{
[self task:1];
});
dispatch_sync(serialQueue, ^{
[self task:2];
});
dispatch_sync(serialQueue, ^{
[self task:2];
});
NSLog(@"结束同步串行任务————————当前线程:%@", [NSThread currentThread]);
}

输出日记

1
2
3
4
5
6
7
8
开始同步串行任务————————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
结束同步串行任务————————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}

在同步中执行串行任务队列,不会创建新的线程,而且等待前一个任务执行完成之后才会去执行下一个任务。

同步执行并行任务队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(void)syncConcurrent{
dispatch_queue_t concurrentQueue = dispatch_queue_create("LJ_CONCURRENT_QUEUE", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"开始同步并行任务————————当前线程:%@", [NSThread currentThread]);
dispatch_sync(concurrentQueue, ^{
[self task:1];
});
dispatch_sync(concurrentQueue, ^{
[self task:2];
});
dispatch_sync(concurrentQueue, ^{
[self task:3];
});
NSLog(@"结束同步并行任务————————当前线程:%@", [NSThread currentThread]);
}
1
2
3
4
5
6
7
8
开始同步并行任务————————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务3————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务3————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
结束同步并行任务————————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}

和同步串行任务的输出结果一模一样。也就是在同步任务中,串行队列和并行队列的执行结果都是一样的:不会创建新的线程,任务依次执行完成才会去执行下一个任务。

异步执行串行任务队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(void)asyncSerial{
dispatch_queue_t serialQueue = dispatch_queue_create("LJ_SERIAL_QUEUE", DISPATCH_QUEUE_SERIAL);
NSLog(@"开始异步串行任务————————当前线程:%@", [NSThread currentThread]);
dispatch_async(serialQueue, ^{
[self task:1];
});
dispatch_async(serialQueue, ^{
[self task:2];
});
dispatch_async(serialQueue, ^{
[self task:2];
});
NSLog(@"结束异步串行任务————————当前线程:%@", [NSThread currentThread]);
}
1
2
3
4
5
6
7
8
开始异步串行任务————————当前线程:<NSThread: 0x600003859400>{number = 1, name = main}
结束异步串行任务————————当前线程:<NSThread: 0x600003859400>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务1————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务2————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务2————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务2————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务2————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}

在异步执行串行任务时,会新建一个线程出来专门执行这个队列的任务。这些任务也是依次执行完成之后才能执行下一个任务。

异步执行并行任务队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(void)asyncConcurrent{
dispatch_queue_t concurrentQueue = dispatch_queue_create("LJ_CONCURRENT_QUEUE", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"开始异步并行任务————————当前线程:%@", [NSThread currentThread]);
dispatch_async(concurrentQueue, ^{
[self task:1];
});
dispatch_async(concurrentQueue, ^{
[self task:2];
});
dispatch_async(concurrentQueue, ^{
[self task:3];
});
NSLog(@"结束异步并行任务————————当前线程:%@", [NSThread currentThread]);
}
1
2
3
4
5
6
7
8
开始异步并行任务————————当前线程:<NSThread: 0x600001182940>{number = 1, name = main}
结束异步并行任务————————当前线程:<NSThread: 0x600001182940>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x6000011ec080>{number = 3, name = (null)}
任务1————当前线程:<NSThread: 0x6000011cfc80>{number = 4, name = (null)}
任务3————当前线程:<NSThread: 0x6000011e0780>{number = 5, name = (null)}
任务2————当前线程:<NSThread: 0x6000011ec080>{number = 3, name = (null)}
任务3————当前线程:<NSThread: 0x6000011e0780>{number = 5, name = (null)}
任务1————当前线程:<NSThread: 0x6000011cfc80>{number = 4, name = (null)}

在异步执行并行任务时,不同的任务会创建不同的线程来执行,而且每个任务的执行都是并发的。

在执行异步任务时,不管并行还是串行任务,都会创建新的线程。而串行是只为整个任务队列创建一个线程,而并行则会为队列里面的每个任务都新建一个线程。

总结

在同步执行中,不管是串行队列还是并行队列,其行为都是一样的:都不会创建新的线程,任务都是依次完成在执行下一个。
在异步执行中,会为整个串行任务队列创建一个线程;并依次执行完任务、在并行任务队列中,会为每个任务都创建一个新的线程,并随机开始执行。
在iOS中,同步和异步的区别在于:是否基于原来的线程执行任务队列。同步是基于,而异步是新建一个线程。串行和并行的区别也在于:是否为每个人任务都新建一个线程来执行。串行是不会的,而并行是会为队列中的每个任务都新建一个线程。基于此,iOS开发中,并发编程的基础还是基于多线程本身的。

主队列和主线程

每个APP都有且仅有一个主线程和一个主队列,它们随着APP的启动而创建,关闭而销毁。主队列是一个串行队列,它只能在主线程中执行。
获取主队列的方法:dispatch_get_main_queue
判读当前线程是否是主线程:NSThread.isMainThread。isMainThread是一个类属性。
因为主队列只能在主线程中执行,所以就算使用异步执行主队列中的任务,也不会创建新的线程。如以下示例:

1
2
3
4
5
6
if (NSThread.isMainThread) {
NSLog(@"当前线程为主线程:%@", [NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"主队列异步任务:%@", [NSThread currentThread]);
});
}

1
2
当前线程为主线程:<NSThread: 0x600001361400>{number = 1, name = main}
主队列异步任务:<NSThread: 0x600001361400>{number = 1, name = main}

一般来说与APP界面更新有关和ViewController生命周期相关的都是在主线程中执行的。

不能在主线程中同步执行主队列中的任务,会引发线程死锁效应。

1
2
3
4
5
6
if (NSThread.isMainThread) {
NSLog(@"当前线程为主线程:%@", [NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"主队列异步任务:%@", [NSThread currentThread]);
});
}

1
2
当前线程为主线程:<NSThread: 0x60000002ac00>{number = 1, name = main}
(lldb)

以上,程序运行崩溃。

文章目录
  1. 1. 多线程的一些相关概念
    1. 1.1. 进程和线程:操作系统学习笔记之进程与线程
    2. 1.2. 同步和异步
    3. 1.3. 串行和并行
  2. 2. 4种多线程操作情况
    1. 2.1. 同步执行串行任务队列
    2. 2.2. 同步执行并行任务队列
    3. 2.3. 异步执行串行任务队列
    4. 2.4. 异步执行并行任务队列
    5. 2.5. 总结
  3. 3. 主队列和主线程