多线程的优缺点:
优点:
1,简化了编程模型
2,更加的轻量级
3,提高了执行效率
4,提高了资源利用率
缺点:
1,增加了程序设计复杂性
2,占用内存空间
3,增大了CPU的调度开销
多线程的实现方法:
- pThread
- NSThread
- GCD
- NSOperation
PThread: 是基于C框架的,在iOS中不常用;
创建方法及基础用法如下:
#pragma mark - PThread
- (void)clickPThread {
NSLog(@"我在主线程中执行");
//参数一:pthread指针 参数二:没什么用 设置成null 参数三:任务执行的方法 参数四:没什么用 设置成null
pthread_t pthread;
pthread_create(&pthread, NULL, run, NULL);
}
//C语言写法
void *run(void *data) {
NSLog(@"我在子线程中执行");
for (int i = 1; i < 10; i++) {
NSLog(@"%d",i);
sleep(1);
}
return NULL;
}
在控制台打印出来的信息可见:
1561449723563.jpg
其中打印日志中threadTest[26985:4948615] ,26985是进程的ID,而进程ID的后面4948615则是当前进程中的一个线程的线程ID,由此可知是在不同的线程中执行的。
NSThread: 苹果封装后的,并且是完全面向对象的,便于操作对象
有两种创建方式:
- 第一种 alloc init方式
#pragma mark - NSThread
- (void)clickNSThread {
NSLog(@"我在主线程中执行");
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(runThread1) object:nil];
[thread1 start];
}
- (void)runThread1 {
NSLog(@"我在子线程中执行");
for (int i = 1; i < 10; i++) {
NSLog(@"%d",i);
sleep(1);
}
}
- 第二种 detachNewThreadSelector 方式
[NSThread detachNewThreadSelector:@selector(runThread1) toTarget:self withObject:nil];
- 第三种 performSelectorInBackground 方式
[self performSelectorInBackground:@selector(runThread1) withObject:nil];
以上三种NSThread的创建方法中,二和三是没有创建相应的对象,无法操作其对象的属性,比如给NSThread的属性设置线程名字,可以更好的进行问题的定位。还可以设置优先级,如下图中,则thread2先执行,thread1后执行
- (void)clickNSThread {
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(runThread1) object:nil];
[thread1 setName:@"Name_Thread1"];
[thread1 setThreadPriority:0.5];
[thread1 start];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(runThread1) object:nil];
[thread2 setName:@"Name_Thread2"];
[thread2 setThreadPriority:0.5];
[thread2 start];
}
- (void)runThread1 {
NSLog(@"%@",[NSThread currentThread].name);//输出 Name_Thread1 Name_Thread2
for (int i = 1; i < 10; i++) {
NSLog(@"%d",i);
sleep(1);
}
}
GCD
任务分同步和异步,同步会阻塞线程,异步不会,dispatch_sync 线程同步、dispatch_async线程异步;
队列分串行和并行;
dispatch_get_global_queue是全局并行队列;
下面看简单的例子:
第一个:同步线程并行队列
//线程同步,并行队列
//因为是同步线程,执行起来则是顺序执行的
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"执行 task 1");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 1 ");
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"执行 task 2");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 2 ");
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"执行 task 3");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 3 ");
});
这个例子中,无论执行多少次,打印顺序都是固定的,即
同步线程的并行执行的顺序
再来看第二个例子:异步线程并行队列
//线程异步,并行队列,打印结果不可预期
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"执行 task 1");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 1 ");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"执行 task 2");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 2 ");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"执行 task 3");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 3 ");
});
这个例子中,多次执行会发现,顺序并不是一成不变的,可能每次都不一样,无规律可循,即并行执行的特点。打印日志如下(此处我打印了三次)
线程异步的并行队列的执行顺序
再来看第三个例子:异步线程串行队列
//第二个参数传NULL,则创建出的是串行队列
//串行队列对应一个线程
dispatch_queue_t queue = dispatch_queue_create("com.gcd.test", NULL);
dispatch_async(queue, ^{
NSLog(@"执行 task 1");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 1 ");
});
dispatch_async(queue, ^{
NSLog(@"执行 task 2");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 2 ");
});
dispatch_async(queue, ^{
NSLog(@"执行 task 3");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 3 ");
});
打印结果如下:
image.png
如上图可发现,是在同一进程同一线程中顺序执行的,即串行队列对应一个线程。
第四个例子:异步线程并行队列
//手动创建的并行队列
dispatch_queue_t queue = dispatch_queue_create("com.gcd.test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"执行 task 1");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 1 ");
});
dispatch_async(queue, ^{
NSLog(@"执行 task 2");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 2 ");
});
dispatch_async(queue, ^{
NSLog(@"执行 task 3");
[NSThread sleepForTimeInterval:2];
NSLog(@"end task 3 ");
});
打印结果如下:
线程异步的并行队列的执行顺序
每次打印结果都会不同,且可以看出是在同一进程的不同线程中执行,即并行队列对应不同线程。
在iOS中是无法使用 dispatch_sync(dispatch_get_main_queue()
dispath向主队列加一个同步的block;
此时主队列在等待 dispatch_sync(dispatch_get_main_queue(),^(){block体});执行
dispatch_sync在等待主队列执行完毕。
所以在此过程中,我们最初调用的dispatch_sync函数一直得不到返回,main queue被阻塞,而我们的block又需要等待main queue来执行它。造成死锁。
NSOperation
NSOperation本身是一个基类,所以要使用其子类进行才能执行任务和线程。
两种使用方式:
1.系统封装好的NSInvocationOperation & NSBlockOperation;
这两个子类通过直接对象调start的方式是串行执行的,
若将其对象通过方法添加到队列中,则可以实现并行执行;
2.自定义类继承NSOperation
网友评论