上一节中,我转载他人的文章,对多线程的理论知识进行了大致的描述,如果想了解的话,请点击这里。接下来的几节内容,我将一一介绍各自的使用。
1. NSThread相关的主要方法:
创建、启动线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [thread start]; // 线程一启动,就会在线程thread中执行self的run方法
- (BOOL)isMainThread; // 是否为主线程 + (BOOL)isMainThread; // 是否为主线程
NSThread *current = [NSThread currentThread]; - (void)setName:(NSString *)name; - (NSString *)name;
创建线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
[self performSelectorInBackground:@selector(run) withObject:nil];
3. 互斥锁
@synchronized(锁对象) { // 需要锁定的代码 }
注意:锁定1份代码只用1把锁,用多把锁是无效的
@property (assign, atomic) int age; - (void)setAge:(int)age { @synchronized(self) { _age = age; } }
什么叫做线程间通信
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
6. Demo 演示
业务描述(卖票): 模拟两个线程抢夺一份资源
运行结果图:
主要代码说明:
1. 属性及方法定义:
/* 1. NSThread 可以使用NSLock 进行加锁工作 2. NSOperation和GCD 应该使用同步锁 :@synchronized(self),并且抢夺的内存资源应该定义为 atomic 的属性 */ @property (atomic,assign) int tickets; @property (atomic,strong) NSLock *lock; // 显示结果区域 @property (weak, nonatomic) IBOutlet UITextView *messageBoard; // 开始售票 - (IBAction)threadSale;
- (IBAction)threadSale { // 1. 先设定销售票的数量 _tickets = 100; // 创建线程1 NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSaleMethod) object:nil]; // 便于跟踪时知道谁在工作 thread1.name = @"售票线程-1"; // 启动线程 [thread1 start]; // 创建线程2 NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSaleMethod) object:nil]; thread2.name = @"售票线程-2"; [thread2 start]; }
- (void)threadSaleMethod { // 1. 定义锁,懒加载 if (_lock == nil) { _lock = [[NSLock alloc] init]; } while(YES) { [_lock lock]; if(_tickets > 0) { NSString *message = [NSString stringWithFormat:@"当前票数是%d,售票线程是%@",_tickets,[[NSThread currentThread] name]]; // 更新UI的工作,一定要放在主线程中完成 // waitUntilDone 的意思是:是否等待主线程更新完毕 [self performSelectorOnMainThread:@selector(appendTextView:) withObject:message waitUntilDone:YES]; _tickets--; // 当前线程执行完毕,解锁 [_lock unlock]; // 模拟延时 if ([[[NSThread currentThread] name] isEqualToString:@"售票线程-1"]) { [NSThread sleepForTimeInterval:0.2]; } else { [NSThread sleepForTimeInterval:0.3]; } } else{ // 在退出之前需要解锁 [_lock unlock]; // 结束信息 NSString *str = [NSString stringWithFormat:@"票已售完%@", [[NSThread currentThread] name]]; [self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES]; break; } } }
- (void)appendTextView:(NSString *)text { NSMutableString *str = [NSMutableString stringWithString:self.messageBoard.text]; [str appendFormat:@"\n%@", text]; self.messageBoard.text = str; // 用来将文本框滚动到想要的位置 // 我们现在想要滚动到最后,这个方法的参数是一个NSRange NSRange range = NSMakeRange(str.length, 1); [self.messageBoard scrollRangeToVisible:range]; }