一、NSThread
是iOS中轻量级得多线程,一个NSThread对象对应一条线程
1、一些类方法
[NSThread mainThread]; // 获取主线程
[NSThread currentThread]; // 获取当前线程
// 阻塞当前线程,设置休眠时间,两种方式实现:
[NSThread sleepForTimeInterval:3];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
[NSThread exit]; // 立即终止主线程之外的所有线程(包括正在执行任务的)
// 注意:需要在掌控所有线程状态的情况下调用此方法,否则可能会导致内存问题。
// threadPriority相关的都已禁用,改用qualityOfService(枚举)代替
[NSThread threadPriority]; // 获取当前线程优先级
[NSThread setThreadPriority:0.5]; // 设置优先级:0.0~1.0;1.0优先级最高
2、创建方式
(1)、alloc init创建,但是需要手动开启
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(network:) object:@{@"name":@"moxiaohui"}];
[thread start];[thread setName:@"moxiaoyan"]; // 线程名称
thread.qualityOfService = NSQualityOfServiceUserInteractive;
// NSQualityOfServiceUserInteractive = 0x21, // 最高优先级, 用于处理 UI 相关的任务
// NSQualityOfServiceUserInitiated = 0x19, // 次高优先级, 用于执行需要立即返回的任务
// NSQualityOfServiceUtility = 0x11, // 普通优先级,主要用于不需要立即返回的任务
// NSQualityOfServiceBackground = 0x09, // 后台优先级,用于处理一些用户不会感知的任务
// NSQualityOfServiceDefault = -1 // 默认优先级,当没有设置优先级的时候,线程默认优先级
thread.stackSize = 8192; // 更改堆栈的大小: 必须 是4KB(1024)的倍数 && 启动线程之前设置 (创建线程是会有开销的)
NSUInteger size = thread.stackSize / 1024; // 所占内存大小
[thread cancel]; // 不会马上退出,做了需要退出的标记
[thread isMainThread]; // 是否是主线程
[thread isFinished]; // 是否已经完成
[thread isCancelled]; // 是否已经取消
[thread isExecuting]; // 是否正在执行中- (void)network:(NSDictionary *)info {NSLog(@"执行 %@", [NSThread currentThread]);NSLog(@"info: %@", info);sleep(2);NSLog(@"完成");
}
(2)、初始化一个子线程,特点:自动开启,是类方法
@autoreleasepool {[NSThread detachNewThreadSelector:@selector(network:) toTarget:self withObject:@{@"name":@"moxiaohui"}];
}
(3)、隐式创建
// 子线程中执行:(耗时操作)
[self performSelectorInBackground:@selector(network:) withObject:@{@"name":@"moxiaohui"}];
// 主线程中执行:(执行更新UI之类得操作)
[self performSelectorOnMainThread:@selector(complete) withObject:nil waitUntilDone:YES];
// 指定线程中执行
[self performSelector:@selector(complete) onThread:[NSThread mainThread] withObject:@{@"name":@"moxiaohui"} waitUntilDone:YES];
// 当前线程中执行
[self performSelector:@selector(network:) withObject:@{@"name":@"moxiaohui"}];
[self performSelector:@selector(network:) withObject:@{@"name":@"moxiaoyan"} withObject:@{@"name":@"moxiaohui"}];
[self performSelector:@selector(afterDelay:) withObject:@{@"name":@"moxiaoyan"} afterDelay:5]; // 5s后执行
// cancel 某一个方法
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(afterDelay:) object:@{@"name":@"moxiaoyan"}];
// cancel 当前对象所有perform方法
[NSObject cancelPreviousPerformRequestsWithTarget:self];- (void)afterDelay:(NSDictionary *)info {NSLog(@"afterDelay info:%@", info);
}- (void)complete {NSLog(@"Update UI");
}
二、需要手动加锁(线程同步)(缺点)
很多大神举的例子哈,我借鉴一下:多窗口买票的情况,不加锁,数据会错乱
#pragma mark - 多窗口买票
- (void)MultiWindowTicket {self.totalTicketCount = 20;_thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];_thread1.name = @"窗口1";_thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];_thread2.name = @"窗口2";_thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];_thread3.name = @"窗口3";[_thread1 start];[_thread2 start];[_thread3 start];
}- (void)saleTicket {while (YES) { // 模拟还有票会持续`-1`的操作
// @synchronized (self) { // 互斥锁:swift 用 objc_sync_enter(self) 和 objc_sync_exit(self)if (self.totalTicketCount > 0) {self.totalTicketCount--;NSLog(@"买了一张,还剩:%ld %@", (long)self.totalTicketCount, [NSThread currentThread]);[NSThread sleepForTimeInterval:0.5];} else {NSLog(@"票买完了");break;}
// }}
}
// 执行结果:
// 买了一张,还剩:7 <NSThread: 0x600003a41a00>{number = 7, name = 窗口1}
// 买了一张,还剩:5 <NSThread: 0x600003a41a80>{number = 9, name = 窗口3}
// 买了一张,还剩:6 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
// 买了一张,还剩:3 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
// 买了一张,还剩:4 <NSThread: 0x600003a41a80>{number = 9, name = 窗口3}
// 买了一张,还剩:4 <NSThread: 0x600003a41a00>{number = 7, name = 窗口1}
// 买了一张,还剩:2 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
// 买了一张,还剩:2 <NSThread: 0x600003a41a80>{number = 9, name = 窗口3}
// 买了一张,还剩:1 <NSThread: 0x600003a41a00>{number = 7, name = 窗口1}
// 买了一张,还剩:0 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
// 票买完了 <NSThread: 0x600003a41a80>{number = 9, name = 窗口3}
// 票买完了 <NSThread: 0x600003a41a00>{number = 7, name = 窗口1}
// 票买完了 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
从执行结果可以看的出来,会有多个窗口在售出一张票后,结算的剩余票数是一样的(也就是说他们把同一张票卖给了多个人)
所以NSThread是线程不安全的,需要程序猿自己手动加锁,保持线程同步!!!
三、多线程总结
GCD | NSOperation | NSThread | |
---|---|---|---|
实现 | C | OC | OC(Pthread基于C实现) |
生命周期 | 自动管理 |
自动管理 |
程序猿管理 |
轻量级别 | 中 | 重 | 轻 |
性能 | 高 | 中 | 低 |
其他 | 跟Block结合代码简洁 | 多了些实用功能 (如:顺序设置、随时取消...) |
简单易用(无需做过多设置), 更直观操作线程对象 |
面向对象 | 面向对象 |