多线程技术是有多套解决方案的,那么我们该如何选择呢?
技术方案 | 简介 | 语言 | 线程生命周期 | 使用频率 |
pthread |
1.一套通用的多线程API 2.适用于UNIX,linux,windows等 3.跨平台,可移植 4.使用难度较高 |
C | 程序员管理 | 几乎不用 |
NSThread |
1.更加面向对象 2.简单易用,可直接操作线程对象 |
OC | 程序员管理 | 偶尔使用 |
GCD |
1.用来替代NSThread等线程技术 2.充分利用设备的多核 |
C | 自动管理 | 经常使用 |
NSOperation |
1.基于GCD(底层是GCG) 2.比GCD多了一些更简单适用的功能 3.使用更加面向对象 |
OC | 自动管理 | 经常使用 |
我们来看看具体的使用,先来说pthread。他表示可移植操作系统接口(portable opration system interface)posix
1.pthread技术
pthread是posix的多线程开发框架,是跨平台的C语言框架。
这个我们可以不用去关系,接下来我们看NSThread技术。
我们来看代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"主线程",[NSThread currentThread]); //方法1:通过NSThread的对象方法
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"方式1"];
[thread start]; //方法2:没有start方法,隐式创建并且启动,但没有线程名字。
[self performSelectorInBackground:@selector(demo:) withObject:@"方法2"]; //方法3 detachNewThreadSelectora方法不需要手动启动,会自动启动并执行selector方法
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"方法3"];
} -(void)demo:(NSString*)str{
NSLog(@"%@, %@",str,[NSThread currentThread]);
}
打印出的结果如下:
2019-03-28 15:28:12.352283+0800 wftest[1631:26090] 主线程
2019-03-28 15:28:12.353628+0800 wftest[1631:26443] 方式1, <NSThread: 0x600002736540>{number = 3, name = (null)}
2019-03-28 15:28:12.353680+0800 wftest[1631:26445] 方法3, <NSThread: 0x600002736740>{number = 5, name = (null)}
2019-03-28 15:28:12.361688+0800 wftest[1631:26444] 方法2, <NSThread: 0x600002736500>{number = 4, name = (null)}
接着,我们来看看线程的属性。
1.name.线程的名字。我们看到上面打印出来的LOG。发现线程都没有名字的。现在我们给thread加上名字。
在第一个方式里,加上thread.name = @"thread1";再进行打印:
2019-03-28 15:35:17.221996+0800 wftest[1911:30827] 主线程
2019-03-28 15:35:17.222669+0800 wftest[1911:30951] 方法3, <NSThread: 0x600003963980>{number = 5, name = (null)}
2019-03-28 15:35:17.222680+0800 wftest[1911:30949] 方式1, <NSThread: 0x600003963780>{number = 3, name = thread1}
2019-03-28 15:35:17.224179+0800 wftest[1911:30950] 方法2, <NSThread: 0x600003963740>{number = 4, name = (null)}
我们发现,第一个方式里的thread已经有了名字。
但是,你也许注意到了。我们两次打印,线程的运行顺序,却不一样。
接着,我们看线程的另一个属性,threadPriority也就是线程的优先级。
取值0-1.0
1.0表示优先级最高
0表示最低
默认的值为0.5
此时我们再给代码加上优先级看下。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"主线程",[NSThread currentThread]); //方法1:通过NSThread的对象方法
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"方式1"];
thread.name = @"thread1";
thread.threadPriority = 1.0;
[thread start]; //方法2:没有start方法,隐式创建并且启动,但没有线程名字。
[self performSelectorInBackground:@selector(demo:) withObject:@"方法2"]; //方法3 detachNewThreadSelectora方法不需要手动启动,会自动启动并执行selector方法
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"方法3"];
} -(void)demo:(NSString*)str{
NSLog(@"%@, %@",str,[NSThread currentThread]);
}
看打印结果:
2019-03-28 15:43:21.467869+0800 wftest[2178:35176] 主线程
2019-03-28 15:43:21.468453+0800 wftest[2178:35234] 方式1, <NSThread: 0x600002ce3780>{number = 3, name = thread1}
2019-03-28 15:43:21.469110+0800 wftest[2178:35236] 方法3, <NSThread: 0x600002ce3980>{number = 5, name = (null)}
2019-03-28 15:43:21.470449+0800 wftest[2178:35235] 方法2, <NSThread: 0x600002ce3740>{number = 4, name = (null)}
可以看到,方法1里的thread果然被率先执行了。
再来看thread的另一个属性,stacksize 栈区大小。
isMainThread是否是主线程
再来看有关资源共享引发的资源抢夺问题。
因为多个线程共享资源,当多个线程共同访问同一块资源时候,比如同一个对象,同一个数据,就会引发数据错乱问题,比如售票系统。
这个时候就需要我们用到线程中的互斥锁了。我们来看代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_count = 50;
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(sellTickets) object:nil];
thread1.name = @"thread1";
[thread1 start]; NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(sellTickets) object:nil];
thread2.name = @"thread2";
[thread2 start]; } -(void)sellTickets{
while (YES) {
//互斥锁
@synchronized (self) {
if(_count>0){
_count = self.count-1;
NSLog(@"剩余票数:%ld--------%@",_count,[NSThread currentThread]);
}else{
NSLog(@"抱歉,票已经售完");
break;
}
}
}
}
这里面有个一个互斥锁,互斥锁的意思就是说,当发现有其他的线程正在执行这段被锁定的代码的时候,线程会进入休眠状态。等待其他线程执行完毕后,锁打开,这个休眠的线程就会被唤醒。
实际上,除了互斥锁之外,还有一个锁,是自旋锁。自旋锁的意思就是说当发现其他线程正在执行这段被锁定的代码的时候,线程会以死循环的方式,一直等待锁定代码执行完成。
线程安全:
当多个线程进行读写操作的时候,仍然能够得到一个正确的结果。被称为线程安全,要实现线程安全,就必须使用锁。
为了得到更好的用户体验,所以,UIKIT不是线程安全的,所以更新UI的操作,必须到主线程去执行。主线程又被称为UI线程。
有关NSTread的内容就说到这里,下次说IOS多线程中,常用的GCD技术。