目录
一、基础理论
二、结合案例使用
前言
多线程这个话题在网上搜的话直接一堆堆的,在这里我也就不瞎比比了,直接上正菜靠谱、实在一点。所谓上正菜就是在实际项目用到的,不是谈一些子虚乌有的东西。
一、基础理论
NSThread 是苹果官方提供的,面向对象,简单易用,可以直接操作线程对象。不过也需要需要程序员自己管理线程的生命周期(主要是创建),我们在开发的过程中偶尔使用 NSThread。比如我们会经常调用[NSThread currentThread]来显示当前的进程信息。
线程: 指独立执行的代码段
进程:指一个正在运行的可执行程序,它可以包含多个线程
主线程:
1.一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”。
2.处理UI事件,刷新显示UI。
使用注意:
1.不要将耗时操作放到主线程中去处理,会卡住线程。
2.和UI相关的刷新操作必须放到主线程中进行处理。
二、结合案例使用
2.1 NSThread
案例一:子线程下载图片->主线程显示图片
- (IBAction)demo {
//创建线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(loadImgageDataFormNetwork) toTarget:self withObject:nil];
}
- (void)loadImgageDataFormNetwork{
NSLog(@"--%@",[NSThread currentThread]);// --<NSThread: 0x604000460040>{number = 3, name = (null)}
NSURL *url = [NSURL URLWithString:@"http://upload-images.jianshu.io/upload_images/1658521-929b88123cf7156c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
[self performSelectorOnMainThread:@selector(updataImg:) withObject:image waitUntilDone:YES];
}
- (void)updataImg:(UIImage *)image{
NSLog(@"%@",[NSThread currentThread]); ///<: 0x600000077680>{number = 1, name = main}
self.imgView.image = image;
}
案例二:售票的小故事(窗口1和窗口2同时售票,售完即止)
引出问题:多个线程访问同一块资源会发生什么??请继续往下看
原汁原味(非线程安全)
- (IBAction)demo1 {
_ticket1 = [[NSThread alloc]initWithTarget:self selector:@selector(buyTicketNoSafe) object:nil];
_ticket1.name = @"1号售票窗口";
_ticket2 = [[NSThread alloc]initWithTarget:self selector:@selector(buyTicketNoSafe) object:nil];
_ticket2.name = @"2号售票窗口";
[_ticket1 start];
[_ticket2 start];
}
- (void)buyTicketNoSafe{
while (1) {
if (_ticketTotal>0) {
_ticketTotal --;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", _ticketTotal, [NSThread currentThread].name]);
[NSThread sleepForTimeInterval:0.2];
}else{
NSLog(@"不好意思。。票买完了。。。。");
break;
}
}
}
改进版(线程安全)
线程安全解决方案:
加锁(在一个线程执行该操作的时候,不允许其他线程进行操作)
- (void)buyTicketSafe{
while (1) {
@synchronized(self){
if (_ticketTotal>0) {
_ticketTotal --;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", _ticketTotal, [NSThread currentThread].name]);
[NSThread sleepForTimeInterval:0.2];
}else{
NSLog(@"不好意思。。票买完了。。。。");
break;
}
}
}
}
*Thread小结:
线程安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作,iOS 实现线程加锁有很多种方式。例如:@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各种方式。这里采取synchronized来保证线程安全,从而解决线程同步问题。
@synchronized
也许已经有小伙伴注意到了只是加了一个这个,就解决了售票bug的问题。
@synchronized
简称为互斥锁,给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。是为了防止多个线程抢夺同一个资源造成的数据安全问题。
或者使用@autoreleasepool
也是一样的。也能解决该问题。
@autoreleasepool
是自动释放池,写在@autoreleasepool
代码块中,让每次执行完成后可以及时的释放临时对象的内存。