常驻线程
在实际的项目中,根据需求咱们可能须要在后台常驻一个线程作一些事情。而对于常驻线程
搜索下的话会有不少解决方案,可是大多数都是提到使用NSThread
和RunLoop
来实现的。而在本篇中介绍另一种实现方法,那就是采用信号量
的方式来实现。什么是信号量?我这里简单的解释下objective-c
信号量
主要是用在多线程的场景,一个线程等待信号直到接收到信号继续运行,另一个线程发送信号通知等待的线程继续运行。数组
信号量
的运行方式正好跟咱们须要的常驻线程
的需求是契合的。在没有任务的时候让该线程处于等待状态,等有任务了,那么发送一个信号让常驻线程
执行咱们的任务。安全
下面是实现的代码:多线程
/**
常驻线程
*/
@interface ResidentThread : NSObject
-(void)doAction:(dispatch_block_t)action;
-(void)cancel;
@end
@implementation ResidentThread{
NSMutableArray *actions;
NSThread *thread;
dispatch_semaphore_t sem;
bool cancel;
}
-(id)init{
self = [super init];
actions = [NSMutableArray array];
// 建立信号量
sem = dispatch_semaphore_create(0);
// 建立一个新线程
thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
return self;
}
-(void)run{
while (true) {
// 等待信号量
dispatch_semaphore_wait(sem, -1);
// 收到信号
// 若是线程已经取消了,那么退出循环
if(cancel){
break;
}
// 开始执行任务
dispatch_block_t block = [actions firstObject];
if(block){
[actions removeObject:block];
block();
}
}
}
// 执行某个任务
-(void)doAction:(dispatch_block_t)action{
if(!cancel){ // 若是线程已经cancel了,那么直接忽略
// 将任务放入数组
[actions addObject:[action copy]];
// 发送信号
dispatch_semaphore_signal(sem);
}
}
// 终止常驻线程
-(void)cancel{
cancel = YES;
// 线程取消后,清空全部的回调
[actions removeAllObjects];
// 至关于发送一个终止任务的信号
dispatch_semaphore_signal(sem);
}
@end
复制代码
上面代码中使用了GCD
的信号量,原本想使用semaphore.h
头文件中的信号量API的,可是这里面的API已经被苹果DEPRECATED
了,就算你强制使用,那么也没法初始化信号量。ide
下面分析代码: ResidentThread
对外总共提供了两个方法,分别是doAction:
和cancel
。oop
doAction:
:这个方法接收一个dispatch_block_t
的参数,表明实际须要让常驻线程执行的任务。cancel
:终止该常驻线程。
为了安全起见,这里采用的是标记位的方式。另外要注意到,cancel方法里面额外发送了一个信号,这个信号的做用相似于
发送了一个终止任务的信号
。spa
从上面的代码中能够看出,常驻线程
的原理是很简单的,并且在实现上也很简单。而且你会发现,这里面的任务实际上是串行
执行的。线程
Runloop
Runloop
的原理我就很少介绍了,掘金里面有不少分析文章,讲的也很透彻,甚至有些直接把源码都贴出来分析。code
从上面的常驻线程
的代码中能够看出来,run
方法的内部就是一个while
循环,循环
等待信号,当接收到信号后立马执行任务。执行完任务后继续等待信号。这样一个流程实际上是跟Runloop
的工做原理是差很少的。Runloop
也是一个等待信号>收到信号>执行回调>继续等待信号,这样一个流程。rem
事实上,这样一套代码,在其余语言中好比C、C++上差很少就是这样实现。
若是进一步的发散下呢?GCD
的任务调度的实现是否是也相似这样呢?事实上,我曾今在写C的时候,就是使用上面的代码实现多线程的任务调度功能。