常駐線程
在實際的項目中,根據需求咱們可能須要在後臺常駐一個線程作一些事情。而對於常駐線程
搜索下的話會有不少解決方案,可是大多數都是提到使用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的時候,就是使用上面的代碼實現多線程的任務調度功能。