1.什么是进程,什么是线程?
进程是一个正在运行的应用程序,一个应用程序可以对应一个或多个进程。应用程序是一个没有生命的实体,只有运行以后,才能称为一个活动的实体,也就是进程。
进程是操作系统分配资源的基本单元。进程在运行的过程中拥有独立的内存单元,一个进程崩溃后,不会对其他进程造成影响。
线程是独立运行和独立调度的基本单位,线程才是程序真正的执行单元负责代码的执行。一个进程可以有一个或多个线程,同一个进程的线程共享进程的内存资源。线程没有单独的地址空间,一个线程崩溃整个进程就会崩溃。
2.什么是多线程?
多线程的实现原理:事实上,同一时间内单核的CPU只能执行一个线程,多线程是CPU快速的在多个线程之间进行切换(调度),造成了多个线程同时执行的假象。
如果是多核CPU就真的可以同时处理多个线程了。
多线程的目的是为了同步完成多项任务,通过提高系统的资源利用率来提高系统的效率。
3.多线程有什么优点和缺点?
多线程可以提高系统的资源利用率,从而提高系统的效率。
开启多线程需要花费时间和空间,开启过多的线程反而会降低性能,cpu频繁的在多个线程中调度会消耗大量的CPU资源,把CPU累死。所以,不要在系统中同时开启过多的子线程。
4.线程创建实际的开销在内存方面是有多大?
苹果官方文档说,开启线程需要消耗内存资源和性能。每一个线程都需要分配系统内核空间和程序的内存空间。
简单来说,创建线程需要花费大约90微秒的时间和512KB的栈空间,以及1KB的内核空间。
5.多线程在开发中的使用场景?
在实际开发中应该将一些耗时的操作放在子线程执行,iOS中默认有一个主线程,用来响应用户的手势和刷新UI,如果在主线程执行耗时操作就会把页面卡死,直到执行完了这个操作才能操作界面。一定要在主线程刷新UI的原因:iOS为了保证效率,多线程是线程不安全的,在子线程刷新UI可能会导致未知错误。
6.iOS中实现多线程的几种方案,各自有什么特点?
Pthread 是用c语言实现的,非常底层的多线程实现方案,在开发中很少用到。需要程序员手动管理线程的生命周期(手动创建和销毁)。
NSThread 面向对象的,需要程序员手动创建线程,但不需要手动销毁。子线程间通信很难。
创建子线程:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(testThread:) object:@"我是参数"];
//有返回值,可以拿到返回值设置线程的优先级和名称
[thread start];//需要手动开启
[NSThread detachNewThreadSelector:@selector(testThread:) toTarget:self withObject:@"便利构造器方式"];//没有返回值,不需要手动开启
[self performSelectorInBackground:@selector(run:)withObject:@"开启后台线程"];//没有返回值,不需要手动开启,只要是NSObject对象或者是NSObject的子类对象都可以用这种方法开启子线程,用下面的方法回到主线程
回到主线程:
[self performSelectorOnMainThread:@selector(backMainThread) withObject:nil waitUntilDone:YES];//回到主线程的方法
GCDc语言,充分利用了设备的多核,自动管理线程生命周期。比NSOperation效率更高。
gcd的用法很多,不写了
NSOperation基于gcd封装,更加面向对象,比gcd多了一些功能。
两个概念:操作(NSOperation)和队列(NSOperationQueue)
创建队列:
NSOperationQueue*mainQueue = [NSOperationQueuemainQueue];//创建主队列,串行队列
NSOperationQueue*queue = [[NSOperationQueue alloc]init];//创建非主队列,同时具备串行队列和并发队列的功能。可以设置最大并发数,实现串行和并行的功能。
创建任务
NSInvocationOperation* opt1 = [[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(task1)object:nil];
[queueaddOperation:opt1];//不需要手动启动队列
NSBlockOperation*blockOperation = [NSBlockOperationblockOperationWithBlock:^{NSLog(@"block --- %@",[NSThreadcurrentThread]); }];
[queue addOperation:blockOperation];
也可以不创建任务,直接用block向队列添加任务:
[mainQueue addOperationWithBlock:^{
self.myImage.image= image;
NSLog(@"ui-------%@",[NSThreadcurrentThread]);
}];
原文链接:https://blog.csdn.net/u013983033/article/details/84346836
ios多线程开发的常用四种方式 (附有demo)
- pthread
- NSThread
- NSOperation\NSOperationQueue
- GCD
pthread
C语言通用的多线程API,跨平台,程序员手动管理线程生命周期,使用难度大
代码实现
//创建线程
NSLog(@"开始执行");
int pthread_create(pthread_t * __restrict ,const pthread_attr_t * __restrict ,void *(*)(void *),void * __restrict);
//使用
pthread_t pthread;
pthread_create(&pthread, NULL, function, NULL);
NSLog(@"执行结束");
void * function(void * param) {
for (int i = 0; i < 5; i ++) {
NSLog(@"i : %d-- 线程: %@",i,[NSThread currentThread]);
}
return NULL;
}
运行结果
2018-11-22 11:49:57.045504+0800 Multithreading[20231:4407861] 开始执行
2018-11-22 11:49:57.045716+0800 Multithreading[20231:4407861] 执行结束
2018-11-22 11:49:57.045828+0800 Multithreading[20231:4409725] i : 0–
线程: <NSThread: 0x6080002724c0>{number = 3, name = (null)} 2018-11-22
11:49:57.046230+0800 Multithreading[20231:4409725] i : 1-- 线程:
<NSThread: 0x6080002724c0>{number = 3, name = (null)} 2018-11-22
11:49:57.047038+0800 Multithreading[20231:4409725] i : 2-- 线程:
<NSThread: 0x6080002724c0>{number = 3, name = (null)} 2018-11-22
11:49:57.047152+0800 Multithreading[20231:4409725] i : 3-- 线程:
<NSThread: 0x6080002724c0>{number = 3, name = (null)} 2018-11-22
11:49:57.047439+0800 Multithreading[20231:4409725] i : 4-- 线程:
<NSThread: 0x6080002724c0>{number = 3, name = (null)}
二、 NSThread
创建NSThread 有三种方法
//公共方法
- (void)threadMoth {
NSLog(@"%@",[NSThread currentThread]);
}
// 方法 1 需要手动开启
NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
[thread start];
//方法 2
[NSThread detachNewThreadSelector:@selector(threadMoth) toTarget:self withObject:nil];
//方法 3
[self performSelectorInBackground:@selector(threadMoth) withObject:nil];
常用的相关方法:
[NSThread mainThread];// 获得主线程
[NSThread isMainThread]; //是否为主线程
NSLog(@"shuchu--%@--%d",[NSThread mainThread],[NSThread isMainThread]);
NSLog(@"休眠前");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4.0]];//阻塞线程 以当前时间为准阻塞4秒
NSLog(@"休眠1");
[NSThread sleepForTimeInterval:2];//阻塞线程 2 秒
NSLog(@"休眠后");
[NSThread exit];// 强制退出线程
方法2、一调用就会立即创建一个线程来做事情;
方法1、需要手动调用 start 启动线程时才会真正去创建线程。
方法3、是利用NSObject的方法 performSelectorInBackground:withObject: 来创建一个线程:在后台运行某一个方法
与 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一样的。
运行结果
2018-11-22 16:17:36.959316+0800 Multithreading[20834:4901061]
<NSThread: 0x60c00007b800>{number = 4, name = (null)} 2018-11-22
16:17:36.959318+0800 Multithreading[20834:4901060] <NSThread:
0x60c00007b8c0>{number = 3, name = (null)} 2018-11-22
16:17:36.960902+0800 Multithreading[20834:4901062] <NSThread:
0x60c00007bc40>{number = 5, name = (null)}
NSOperation、NSOperationQueue
NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。
(1)、NSOperation
NSOperation 是个抽象类,不能用来封装操作。我们只有使用它的子类来封装操作。我们有两种方式来封装操作。
子类 NSInvocationOperation
子类 NSBlockOperation
1、 子类 NSInvocationOperation
创建 NSInvocationOperation 对象
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
调用 start 方法开始执行操作
[operation start];
运行结果
2018-11-22 16:17:36.959446+0800 Multithreading[20834:4899673]
<NSThread: 0x600000075840>{number = 1, name = main}
通过运行结果 可以看到 在没有加入到NSOperationQueue中,而是单独使用的时候,NSInvocationOperation
并没有开启新的线程,还是在主线程中执行的
2、子类NSBlockOperation
创建对象
NSBlockOperation * block = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block-%@",[NSThread currentThread]);
}];
添加额外的操作
[block addExecutionBlock:^{
NSLog(@"block-%@",[NSThread currentThread]);// 打印当前线程
}];
调用 start 方法开始执行操作
[block start];
运行结果
2018-11-22 16:41:36.057252+0800 Multithreading[20893:4948069]
<NSThread: 0x600000074a00>{number = 1, name = main}
和NSInvocationOperation 使用一样。因为代码是在主线程中调用的,所以打印结果为主线程。如果在其他线程中执行操作,则打印结果为其他线程。 通过 addExecutionBlock: 就可以为 NSBlockOperation 添加额外的操作。
(2)、NSOperationQueue
NSOperation 需要配合 NSOperationQueue 来实现多线程, 需要将创建好的操作加入到队列中去。总共有两种方法:
第一种方法:
- (void)addOperation:(NSOperation *)op;
需要先创建操作,再将创建好的操作加入到创建好的队列中去。代码实现
//创建队列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
//添加操作
NSInvocationOperation * operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
NSInvocationOperation * operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
NSBlockOperation * block1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block1-%@",[NSThread currentThread]);// 打印当前线程
}];
[block1 addExecutionBlock:^{
NSLog(@"block2-%@",[NSThread currentThread]);// 打印当前线程
}];
//3 添加队列
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:block1];
运行结果
2018-11-22 17:27:58.671071+0800 Multithreading[23002:5205701]
<NSThread: 0x600000262ac0>{number = 3, name = (null)}
2018-11-22 17:27:58.671073+0800 Multithreading[23002:5205666] queue1-<NSThread: 0x60c000265a80>{number = 5, name = (null)}
2018-11-22 17:27:58.671071+0800 Multithreading[23002:5205664] block1-<NSThread: 0x608000260400>{number = 6, name = (null)}
2018-11-22 17:27:58.671115+0800 Multithreading[23002:5205663] <NSThread: 0x608000260340>{number = 4, name = (null)}
使用 NSOperation 子类创建操作,并使用 addOperation: 将操作加入到操作队列后能够开启新线程,进行并发执行。
第二种方法:
- (void)addOperationWithBlock:(void (^)(void))block;
无需创建操作,在 block 中添加操作,直接将包含操作的 block 加入到队列中。
NSOperationQueue * queue1 = [[NSOperationQueue alloc] init];
[queue1 addOperationWithBlock:^{
NSLog(@"queue1-%@",[NSThread currentThread]);// 打印当前线程
}];
[queue1 addOperationWithBlock:^{
NSLog(@"queue1-%@",[NSThread currentThread]);// 打印当前线程
}];
[queue1 addOperationWithBlock:^{
NSLog(@"queue1-%@",[NSThread currentThread]);// 打印当前线程
}];
[queue1 addOperationWithBlock:^{
NSLog(@"queue1-%@",[NSThread currentThread]);// 打印当前线程
}];
运行结果
2018-11-22 17:27:58.671156+0800 Multithreading[23002:5205665]
queue1-<NSThread: 0x6000002613c0>{number = 7, name = (null)}
2018-11-22 17:27:58.671322+0800 Multithreading[23002:5205664] block2-<NSThread: 0x608000260400>{number = 6, name = (null)}
2018-11-22 17:27:58.671331+0800 Multithreading[23002:5205666] queue1-<NSThread: 0x60c000265a80>{number = 5, name = (null)}
2018-11-22 17:27:58.671367+0800 Multithreading[23002:5205701] queue1-<NSThread: 0x600000262ac0>{number = 3, name = (null)}
使用 addOperationWithBlock: 将操作加入到操作队列后能够开启新线程,进行并发执行。
四、 GCD
GCD会自动利用更多的CPU内核,会自动管理线程的生命周期,不需要写管理线程的代码。定制任务 将任务添加到队列中 GCD会自动将队列中的任务取出,放到线程中去执行,任务的取出遵循FIFO原则。
Main queue:
运行在主线程,由dispatch_get_main_queue获得.和ui相关的就要使用MainQueue,在主线程中更新ui.
Serial quque(private dispatch queue)
每次运行一个任务,可以添加多个,执行次序FIFO.
Concurrent queue(globaldispatch queue):
可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.
同步 在当前线程中执行
dispatch_sync(dispatch_queue_t queue, ^(void)block)
异步 可以在新的线程中执行,有开新线程的能力(不是一定会开新线程,比如放在主队列中)
dispatch_async(dispatch_queue_t queue, ^(void)block)
常用地方
//延迟执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延时2秒执行");
});
//全局异步并发队列
/**
参数 identifier 优先级
参数 flags 保留参数 默认传0
*/
dispatch_queue_t queuet = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queuet, ^{
NSLog(@"queuet --- 1");
});
dispatch_async(queuet, ^{
NSLog(@"queuet --- 2");
});
dispatch_async(queuet, ^{
NSLog(@"queuet --- 3");
});
//自定义并发队列
dispatch_queue_t queuet1 = dispatch_queue_create("www.baidu.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queuet1, ^{
NSLog(@"queuet1 --- 1");
});
dispatch_async(queuet1, ^{
NSLog(@"queuet1 --- 2");
});
dispatch_async(queuet1, ^{
NSLog(@"queuet1 --- 3");
});
dispatch_queue_t queuet2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queuet2, ^{
NSLog(@"queuet2 --- 1");
});
dispatch_barrier_async(queuet2, ^{
NSLog(@"queuet2 --- 2");
});
//group
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"group1");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"group2");
});
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"group3");
});
//创建单例
static dispatch_once_t onceToken;
static ViewController * viewController;
dispatch_once(&onceToken, ^{
viewController = [[ViewController alloc] init];
});
return viewController;
五、线程安全问题
资源共享:
一块资源可能会被多个线程共享,比如多个线程访问同一个对象、对一个变量、同一个文件
解决方法:
互斥锁
@synchronized(锁对象){ //需要锁定的代码 }
或者 加NSLock锁
原子和非原子属性
atomic 默认 原子属性 为setter方法加锁
线程安全,需要消耗大量的资源
nonatomic 非原子属性 不会为setter方法加锁
非线程安全,适合内存小的移动设备
注意:
所有属性都声明为nonatomic
尽量避免多线程抢夺同一块资源
尽量加加锁、资源抢夺的业务逻辑交给服务器处理,减小移动客户端压力
网友评论