首页 > 代码库 > 多线程——NSThread、GCD、NSOperation
多线程——NSThread、GCD、NSOperation
1、前言:
一个应用程序就是一个进程,一个进程至少包含一个线程,程序启动会自动创建一个主线程,负责UI界面的现实和控件事件的监控。多线程可以更充分的利用系统CPU资源,一定程度上提升程序的性能。1个进程可以开启多条线程,每条线程可以并行(同时)执行不同的任务。在一个线程内可以包含多个事物(要干的活),在线程内依据先进先出的特性串行执行……
2、NSThread
- (void)viewDidLoad{ [super viewDidLoad]; NSLog(@"main thread is %@",[NSThread mainThread]);//打印主线程(UI线程)}-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ //新开辟一条子线程,启动子线程的时候调用downLoad方法,object是传递给子线程调用方法的参数 NSThread *thread=[[NSThread alloc] initWithTarget:self selector:@selector(downLoad:) object:@"luseike"]; //给子线程起一个名字 thread.name=@"thread one"; //开始子线程 [thread start];}-(void)downLoad:(NSString *)param{ //打印当前线程(子线程)及其名字和传递过来的参数 NSLog(@"begin downLoad--param is %@--current thread is %@ current name is %@",param,[NSThread currentThread],[NSThread currentThread].name);}
打印结果如下:
2014-06-26 23:03:05.724 NSThread[50292:60b] main thread is <NSThread: 0x8c421c0>{name = (null), num = 1}
2014-06-26 23:03:06.567 NSThread[50292:3807] begin downLoad--param is luseike--current thread is <NSThread: 0xa33c350>{name = thread one, num = 2} current name is thread one
可以看到主线程的num=1,子线程的num=2,证明确实是新开辟了一条线程来执行downLoad操作。
还可以通过isMainThread判断是否是主线程,setThreadPriority:(double)p来设置子线程的优先级,优先级的取值范围在0.0~1.0之间,默认是0.5,值越大,优先级越高,被执行的几率越大
还有两种比较便捷的方式来创建子线程
1、[NSThread detachNewThreadSelector:@selector(downLoad:) toTarget:self withObject:@"luseike"];// 附加一个线程
2、[self performSelectorInBackground:@selector(downLoad:) withObject:@"haha"]; //在后台执行一个线程
这两种方式创建的子线程都不需要调用start方法,系统会自动执行对应的方法,都也都没有机会设置优先级和咸线程名等更详细的设置了,不过这个一般不重要
3、线程的状态
线程从生到死大致有下面几种状态:新建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、死亡(dead)
一个线程被创建之后会放到一个叫做可调度线程池内,等待被CPU调度,当该线程获得CPU的执行权时,就进入到running状态。running状态的线程如果调用sleep方法或者在等待同步锁,就会进入阻塞状态,进入阻塞状态的线程会重新被放到可调度线程池内,等待被重新调度。线程任务执行完毕,或者被强制退出,会进入dead状态,注意:进入dead状态的线程并没有被释放内存,只是不能用了而已,还存在内存中。
4、控制线程的状态
启动线程:之前介绍过,调用start方法
阻塞(暂停)线程:
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
强制停止线程:+(void)exit;
多线程的安全隐患:当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。解决的机制就是使用锁(互斥锁),让多条线程同步执行,这就是传说的线程同步技术。值得注意的是:锁定一份代码只用1把锁,用多把锁是没有意义的;线程同步的前提是多条线程抢占同一块资源
#import "ViewController.h"@interface ViewController ()@property(nonatomic,strong)NSThread *thread1;@property(nonatomic,strong)NSThread *thread2;@property(nonatomic,strong)NSThread *thread3;@property(nonatomic,assign)int totalCount;@end@implementation ViewController- (void)viewDidLoad{ [super viewDidLoad]; self.totalCount=100; self.thread1=[[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.thread2=[[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.thread3=[[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];}-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ [self.thread1 start]; [self.thread2 start]; [self.thread3 start];}-(void)saleTicket{ int leftCount=self.totalCount; while (leftCount>0) { self.totalCount=leftCount--; NSLog(@"%@ sale one ticket,left %d",[NSThread currentThread],leftCount); }
}@end
如上代码,模拟3个子线程同时访问公共资源totalCount,每次操作减一,多线程访问同一资源容易引起的数据安全问题已经说明了,打印部分结果如下
2014-06-26 23:51:33.639 NSThread[63065:360b] <NSThread: 0xa06dd50>{name = (null), num = 3} sale one ticket,left 99
2014-06-26 23:51:33.639 NSThread[63065:3707] <NSThread: 0xa0706e0>{name = (null), num = 2} sale one ticket,left 99
2014-06-26 23:51:33.639 NSThread[63065:3f03] <NSThread: 0xa06ddf0>{name = (null), num = 4} sale one ticket,left 99
2014-06-26 23:51:33.641 NSThread[63065:3707] <NSThread: 0xa0706e0>{name = (null), num = 2} sale one ticket,left 98
2014-06-26 23:51:33.641 NSThread[63065:3f03] <NSThread: 0xa06ddf0>{name = (null), num = 4} sale one ticket,left 98
2014-06-26 23:51:33.642 NSThread[63065:3707] <NSThread: 0xa0706e0>{name = (null), num = 2} sale one ticket,left 97
2014-06-26 23:51:33.641 NSThread[63065:360b] <NSThread: 0xa06dd50>{name = (null), num = 3} sale one ticket,left 98
2014-06-26 23:51:33.642 NSThread[63065:3f03] <NSThread: 0xa06ddf0>{name = (null), num = 4} sale one ticket,left 97
可以看到三个子线程都操作了totalCount变量,每次操作之后的值却没有变化……
互斥锁的使用格式:
@synchronized(锁对象) { // 需要锁定的代码 }
-(void)saleTicket{ while (1) { @synchronized(self) { // 加锁(只能用一把锁) // 1.先检查票数 int count = self.totalCount; if (count > 0) { self.totalCount = count - 1; NSThread *current = [NSThread currentThread]; NSLog(@"%@ sale one ticket, left %d tickets", current, self.totalCount); } else { [NSThread exit]; } } }}
打印结果就可以正常显示了……
很晚了,有时间在写写GCD和NSOperation