每一个iOS应用程序中都有一个主线程用来更新UI界面、处理用户的触摸事件、解析网络下载的数据,因此不能把一些太耗时的操作(比如网络下载数据)放在主线程中执行,不然会造成主线程堵塞(出现界面卡死,防止界面假死),带来极坏的用户体验。iOS的解决方案就是将那些耗时的操作放到另外一个线程中去执行,多线程异步编程是防止主线程堵塞,增加运行效率的最佳方法.
多线程技术:
1.异步下载数据,是多线程技术的一个比较常见的应用场景
2.多线程技术使用场景:app中有耗时的操作或功能(比如吐:1.客户端与服务端交互 2.从数据库中一次性读取大量数据 3.对大量数据的解析过程 等),需要在主线程之外,单独开辟一个新的线程(子线程/工作线程)来执行
iOS所支持的多线程方法主要有:
NSThread
NSOperation & NSOperationQueue
GCD
1.NSThread
1.1NSThread线程的创建
NSThread线程的创建有三种方式:
第一种:创建后台线程
[self performSelectorInBackground:@selector(thread1Click:) withObject:@"线程1"]; performSelectorInBackground内部会创建一个线程 专门 执行调用 -thread1Click:
第二种: 手动开启
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(thread2Click:) object:@"线程2"]; 代码的意思是创建一个线程 执行调用-thread2Click: object:就是前面函数的参数 还可以设置线程的名字 thread2.name=@"线程2" .这种方式创建的线程需要手动启动[thread2 start];
第三种:一旦创建 线程会立即执行
创建代码:[NSThread detachNewThreadSelector:@selector(thread3Click:) toTarget:self withObject:@"线程3"];
1.2监听线程结束
我们这里采用通知的形式.首页我们应该先注册观察者.注册观察者的时候,内部会专门创建一个线程 监听其他线程有木有结束,线程结束,一般会发送一个结束通知 下面是创建通知中心:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadWillEnd:) name:NSThreadWillExitNotification object:nil];
当监听到线程结束的时候调用threadWillEnd:这个函数.最后不要忘记 在dealloc里面删除观察者喔.不管是在MRC或ARC中都是要删除的
1.3线程间的通信
这里直接上代码了
1 @interface ViewController ()
2 {
3 NSThread *_thread1;
4 }
5 @end
6
7 @implementation ViewController
8
9 - (void)viewDidLoad {
10 [super viewDidLoad];
11 _thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(thread1Click:) object:@"线程1"];
12 [_thread1 start];
13
14 NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(thread2Click:) object:@"线程2"];
15 [thread2 start];
16
17 NSThread *thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(thread3Click:) object:@"线程3"];
18 [thread3 start];
19 }
20 - (void)thread1Click:(id) obj {
21 NSLog(@"%s",__func__);
22 while (1) {
23 if ([[NSThread currentThread] isCancelled]) {
24 //要判断 当前这个线程 是否 被取消过(是否发送过取消信号)
25 //如果被取消过,那么我们可以让当前函数结束那么这个线程也就结束了
26
27 //break;//结束循环
28 NSLog(@"子线程1即将结束");
29 //return;//返回 函数
30 [NSThread exit];//线程退出
31 }
32
33 NSLog(@"thread1");
34 [NSThread sleepForTimeInterval:0.5];
35 }
36 NSLog(@"子线程1即将结束");
37 }
38 - (void)thread2Click:(id) obj {
39 NSLog(@"%s",__func__);
40 for (NSInteger i = 0; i < 10; i++) {
41 [NSThread sleepForTimeInterval:0.2];
42 NSLog(@"thread2:_i:%ld",i);
43 }
44 NSLog(@"子线程2即将结束");
45 //当 thread2 即将结束之后 通知 thread1结束
46
47 [_thread1 cancel];// cancel 在这里 只是给_thread1 发送了一个cancel 信号,最终thread1的取消,取决于 thread1的,如果要取消thread1,那么需要在thread1执行调用的函数内部 进行判断
48 }
49 - (void)thread3Click:(id) obj {
50 NSLog(@"%s",__func__);
51 for (NSInteger i = 0; i < 10; i++) {
52 [NSThread sleepForTimeInterval:0.2];
53 NSLog(@"thread3:_i:%ld",i);
54 }
55 NSLog(@"子线程3即将结束");
56 }
1.4对线程加锁
有时候会出现一种情况,两个或多个线程同时对某一个比较重要的资源进行读写修改,当一个线程正在修改的时候,另一个线程也对该资源进行读写,这样就会照成错乱,数据就会失去可靠性.为了解决这个问题,我们采用线程锁的方式来防止这种情况发生.它的主要功能就是在一个线程对当前资源访问的时候,对该资源进行上锁,其他线程就不能在对该资源进行访问了,知道该线程访问结束,解开线程锁,其他资源才可以访问该资源.
当然要想加锁,必须要先创建一把线程锁喽,嘿嘿:
_lock = [[NSLock alloc] init];
- (void)thread1Click:(id)obj {
[_lock lock];//加锁
NSLog(@"thread1开始");
for (NSInteger i = 0 ; i < 10; i++) {
_cnt += 2;//想让 _cnt 连续+2
NSLog(@"thread1_cnt:%ld",_cnt);
[NSThread sleepForTimeInterval:0.2];
}
NSLog(@"thread1即将结束");
[_lock unlock];//解锁 //访问资源结束 解锁
}
- (void)thread2Click:(id)obj {
[_lock lock];
NSLog(@"thread2开始");
for (NSInteger i = 0 ; i < 10; i++) {
_cnt -= 5;//让 _cnt连续-5
NSLog(@"thread2_cnt:%ld",_cnt);
[NSThread sleepForTimeInterval:0.2];
}
NSLog(@"thread2即将结束");
[_lock unlock];
}
在使用线程锁的同时,也要防止产生死锁,就如下面的情况,就是一个线程1访问a,对a加锁,另一个线程2访问b对b加锁,与此同时,线程1也要对b进行访问,线程2也要对a资源访问,但是,线程1要访问b只有等线程2释放b之后才能访问,而线程2要访问a也只有当线程1解锁a之后才能访问,于是乎,彼此都在等对方解锁资源,这样就产生了死锁.应该避免这种问题的产生.
_lock1 lock
访问 a
xxxxxxx
_lock2 lock
访问 b
_lock2 unlock
访问 a
_lock1 unlock
-----------------
_lock2 lock
访问b
xxxxxxx
xxxxxx
_lock1 lock
访问 a
_lock1 unlock
访问 b
_lock2 unlock
此外,用@synchronized (self){}也可以达到加锁的效果
- (void)thread1Click:(id)obj {
// @synchronized (self){} 类似于 加锁和解锁过程
@synchronized(self) { //使线程 对当前 对象进行操作 时,同步进行 ,阻塞线程
//跟加锁原理 是一样的 执行 @synchronized(self) 会判断有没有加锁,加过锁那么阻塞,没有加锁就继续执行
NSLog(@"thread1开始");
for (NSInteger i = 0 ; i < 10; i++) {
_cnt += 2;//想让 _cnt 连续+2
NSLog(@"thread1_cnt:%ld",_cnt);
[NSThread sleepForTimeInterval:0.2];
}
NSLog(@"thread1即将结束");
}
}
- (void)thread2Click:(id)obj {
@synchronized(self) {
NSLog(@"thread2开始");
for (NSInteger i = 0 ; i < 10; i++) {
_cnt -= 5;//让 _cnt连续-5
NSLog(@"thread2_cnt:%ld",_cnt);
[NSThread sleepForTimeInterval:0.2];
}
NSLog(@"thread2即将结束");
}
}