简介
在iOS开发过程中,一共存在三种线程
1、pthread,基本上不用
2、Nsthread,面向对象,偶尔使用
3、GCD,纯C语言,经常使用
4、NSOpearation,是对GCD的封装,面向对象,经常使用
在本篇的介绍中,我们着重介绍NSThread的使用
NSThread创建
一个NSthread就是一个单独的独立线程
NSthread的创建一共可以分为如下三种方式:
创建方式一:
//创建线程的第一种方法,此种方式创建的线程不会自启动,需要调用start来启动线程
// NSThread *thread = [[NSThread alloc] initWithBlock:<#^(void)block#>];这种方式创建的线程的执行代码直接放在代码块里面执行
//先创建对象,然后调用perform的一系列方法,也能执行线程的执行
// NSThread *thread = [[NSThread alloc] init];
// [thread performSelectorInBackground:<#(nonnull SEL)#> withObject:<#(nullable id)#>];
// [thread performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>];
// [thread performSelector:<#(SEL)#> withObject:<#(id)#>];
// [thread performSelector:<#(nonnull SEL)#> withObject:<#(nullable id)#> afterDelay:<#(NSTimeInterval)#>];
// [thread performSelector:<#(nonnull SEL)#> onThread:<#(nonnull NSThread *)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>];
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
[thread start];//启动线程
创建方式二:
- (void) createNSThread2 {
//创建线程的第二种方法,使用此种方法创建的线程会自启动
// [NSThread detachNewThreadWithBlock:<#^(void)block#>];
[NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];
}
创建方式三:
- (void) createNSThread3 {
//创建线程的第三种方式,隐试的创建线程
// [self performSelector:@selector(threadRun) withObject:self afterDelay:0];afterDelay表示可以延时多长时间执行线程
// [self performSelectorOnMainThread:@selector(threadRun) withObject:self waitUntilDone:<#(BOOL)#>];waitUntilDone表示是否等待线程执行完再往下进行,会阻塞当前程序运行
// [self performSelectorInBackground:<#(nonnull SEL)#> withObject:<#(nullable id)#>];在后台运行线程,意味着开辟新线程
[self performSelector:@selector(threadRun) withObject:self];
}
上述创建NSThread的三种方式在使用中最为常见,其中通过第一种创建的线程需要手动调用【thread start】来启动,其余两种创建NSThread线程的方式会自动启动,无需在手动启动线程。
NSThread与调用方法之间通信
如果在创建线程的时候想给调用方法传递参数,可以使用如下方式
[[NSThread alloc] initWithTarget:self selector:@selector(threadRun:) object:@"https://xxxxx.jpg"];
- (void)threadRun:(NSSTting *)url {
}
给线程设置名称和获取当前线程
想要知道程序具体运行在哪个线程当中,或者需要获取当前程序运行的是什么线程;此时需要给线程设置名称
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun:) object:@"https://xxxxx.jpg"];
thread.name = @"xx线程";
然后在调用线程的的方法中通过【NSThread currentThread】来显示当前线程的名称和当前线程的num id号
线程安全
在使用多线程的过程中,享受了线程带来的便捷,自然就要考虑线程带来的一些隐患,比如多个线程共同抢夺一块资源的时候,如何保证线程的安全性?
这里我们模拟一下飞机卖票的情形,多个窗口同时卖票,会出现什么问题?
self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket_notsafe_model) object:nil];
self.thread1.name = @"thread1";
self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket_notsafe_model) object:nil];
self.thread2.name = @"thread2";
self.thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket_notsafe_model) object:nil];
self.thread3.name = @"thread3";
self.thread4 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket_notsafe_model) object:nil];
self.thread4.name = @"thread4";
//开启线程
[self.thread1 start];
[self.thread2 start];
[self.thread3 start];
[self.thread4 start];
while (true) {
if (self.ticket_counts > 0) {
self.ticket_counts -= 1;
NSLog(@"thread.name----%@ tickets_left-------%d",[NSThread currentThread].name, self.ticket_counts);
} else {
return;
}
}
此时就是造成线程对票数资源的抢夺,导致最终卖出去的票数不对,容易产生不必要的损失;面对这样的场景,我们可以使用加锁的方式来解决
一种是使用NSLock
首先声明一个所对象
self.locker = [[NSObject alloc] init];
然后再调用saleTicket的地方使用locker锁对象
while (true) {
@synchronized(self.lock) {
if (self.ticket_counts > 0) {
self.ticket_counts -= 1;
NSLog(@"thread.name----%@ tickets_left-------%d",[NSThread currentThread].name, self.ticket_counts);
} else {
return;
}
}
}
这里有一个需要注意的地方,就是NSLock必须使用的是同一个锁对象
另外一种方法就是使用self加锁
while (true) {
@synchronized(self) {
if (self.ticket_counts > 0) {
self.ticket_counts -= 1;
NSLog(@"thread.name----%@ tickets_left-------%d",[NSThread currentThread].name, self.ticket_counts);
} else {
return;
}
}
}
最后附上以上代码链接
上述所有代码均可以在NSThreadDemo中查看,有需要可以自行获取