多線程一

一、線程方式:NSThread挽拔,NSQueueOperation, GCD, pthread

二但校、僵尸線程問題(NSThead操作)

  • 在子線程添加定時器,如果在主線程中移除定時器會造成僵尸線程状囱。
    1毅戈、在子線程添加定時器到腥,必須要執(zhí)行[[NSRunLoop currentRunLoop] run]播演。因為runloop中的資源沒被移除讶坯。
    2袍镀、在子線程中無法執(zhí)行到NSLog(@"runloop finished")這段代碼。
    3绸吸、觸摸屏幕時设江,將定時器從runloop中移除,但是定時器的時間依舊還在執(zhí)行码俩。
- (void)viewDidLoad {
    [super viewDidLoad];
    NSString * str = @"12211212";
    [NSThread detachNewThreadSelector:@selector(threadEvent:) toTarget:self withObject:str];
}
- (void)threadEvent:(id)thread{
    NSLog(@"%@",[NSThread currentThread]);
    //self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:@"2121" repeats:YES];
    self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:nil repeats:YES];
    //使用 NSRunLoopCommonModes 模式稿存,這個模式相當(dāng)于 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode 的結(jié)合
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    NSLog(@"runloop finished");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.timer invalidate];
}
- (void)timeEvent:(NSTimer *)sender{
    //定時器事件在子線程中執(zhí)行
    NSLog(@"%@",[NSThread currentThread]);
    NSLog(@"%s",__func__);
}

線程依舊存在瞳秽,如圖一所示练俐。


圖一 僵尸線程圖.png
  • 如何解決僵尸線程
    在該線程中移除掉定時器。
- (void)viewDidLoad {
    [super viewDidLoad];
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
    [self.thread start];
}
- (void)threadEvent:(id)thread{
    NSLog(@"%@",[NSThread currentThread]);
    //self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:@"2121" repeats:YES];
    self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:nil repeats:YES];
    //使用 NSRunLoopCommonModes 模式,這個模式相當(dāng)于 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode 的結(jié)合
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    NSLog(@"runloop finished");
}
- (void)timeInvalidate:(id)sender{
    [self.timer invalidate];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
/*
  You cannot cancel messages queued using this method. 
  If you want the option of canceling a message on the current thread, 
  you must use either the performSelector:withObject:afterDelay: or performSelector:withObject:afterDelay:inModes: method.
要想自線程消滅掉丘喻,必須在其子線程中執(zhí)行performSelector:withObject:afterDelay:或者performSelector:withObject:afterDelay:inModes: 方法
*/
    [self performSelector:@selector(timeInvalidate:) onThread:self.thread withObject:@"e" waitUntilDone:YES];
}
- (void)timeEvent:(NSTimer *)sender{
    //定時器事件在子線程中執(zhí)行
    NSLog(@"%@",[NSThread currentThread]);
    NSLog(@"%s",__func__);
}

但是依舊不能解決僵尸線程的問題泉粉,因為在移除定時器的同時榴芳,給runLoop中添加一個timeInvalidate方法。

  • 徹底解決僵尸線程
    1讨彼、方法一:
- (void)viewDidLoad {
    [super viewDidLoad];
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
    [self.thread start];
}
- (void)threadEvent:(id)thread{
    NSLog(@"%@",[NSThread currentThread]);
    //self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:@"2121" repeats:YES];
    self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:nil repeats:YES];
    //使用 NSRunLoopCommonModes 模式柿祈,這個模式相當(dāng)于 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode 的結(jié)合
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
    [self performSelector:@selector(timeInvalidate:) withObject:nil afterDelay:5];
    [[NSRunLoop currentRunLoop] run];
    NSLog(@"runloop finished");
}
- (void)timeInvalidate:(id)sender{
    [self.timer invalidate];
}
- (void)timeEvent:(NSTimer *)sender{
    //定時器事件在子線程中執(zhí)行
    NSLog(@"%@",[NSThread currentThread]);
    NSLog(@"%s",__func__);
}

2、方法二:

- (void)viewDidLoad {
    [super viewDidLoad];
    _flag = NO;
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
    [self.thread start];
}
- (void)threadEvent:(id)thread{
    NSLog(@"%@",[NSThread currentThread]);
    self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeEvent:) userInfo:nil repeats:YES];
    //使用 NSRunLoopCommonModes 模式菩貌,這個模式相當(dāng)于 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode 的結(jié)合
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    NSLog(@"runloop finished");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.flag = YES;
}
- (void)timeEvent:(NSTimer *)sender{
    //定時器事件在子線程中執(zhí)行
    NSLog(@"%@",[NSThread currentThread]);
    NSLog(@"%s",__func__);
    if(self.flag == YES){
        [self.timer invalidate];
        self.timer = nil;
    }
}
  • 在runloop中添加port重荠,如果在其它線程中移除掉,會產(chǎn)生僵尸線程仇参。
- (void)viewDidLoad {
    [super viewDidLoad];
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
    [self.thread start];
}
- (void)threadEvent:(id)thread{
    NSLog(@"start");
    self.myRunLoop = [NSRunLoop currentRunLoop];
    self.port = [[NSPort alloc] init];
    [self.myRunLoop addPort:self.port forMode:NSRunLoopCommonModes];
    [self.myRunLoop run];
    NSLog(@"finished");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.myRunLoop removePort:self.port forMode:NSRunLoopCommonModes];
}
  • 解決在runloop中添加port產(chǎn)生僵尸線程
- (void)viewDidLoad {
    [super viewDidLoad];
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEvent:) object:self];
    [self.thread start];
}
- (void)threadEvent:(id)thread{
    NSLog(@"start");
    self.myRunLoop = [NSRunLoop currentRunLoop];
    self.port = [[NSPort alloc] init];
    [self.myRunLoop addPort:self.port forMode:NSRunLoopCommonModes];
    [self performSelector:@selector(portCancel) withObject:nil afterDelay:5];
    [self.myRunLoop run];
    NSLog(@"finished");
}
- (void)portCancel{
    [self.myRunLoop removePort:self.port forMode:NSRunLoopCommonModes];
}

結(jié)論:
在子線程中冈敛,給runloop添加資源鸣皂,必須在該線程中進(jìn)行釋放,否則會產(chǎn)生僵尸線程寞缝。

三癌压、線程隊列NSQueueOperation

3.1 Operation對象的并發(fā)和非并發(fā)

3.2 系統(tǒng)的Operation對象

3.2.1 NSBlockOperation

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 2;
    NSBlockOperation * blockOperation = [[NSBlockOperation alloc] init];
    [blockOperation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    [queue addOperation:blockOperation];
}

打印結(jié)果:

<NSThread: 0x608000270b40>{number = 5, name = (null)}
<NSThread: 0x608000270940>{number = 4, name = (null)}
<NSThread: 0x608000270b80>{number = 6, name = (null)}
<NSThread: 0x608000270900>{number = 3, name = (null)}
<NSThread: 0x608000270b40>{number = 5, name = (null)}
<NSThread: 0x608000270940>{number = 4, name = (null)}
<NSThread: 0x608000270b80>{number = 6, name = (null)}

3.3.2 NSInvocationOperation(NSInvocation)

  • 使用initWithTarget:selector:object:開啟線程。缺點:只能傳遞一個參數(shù)荆陆。
- (void)viewDidLoad {
    [super viewDidLoad];
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationEvent) object:nil];  
    [queue addOperation:invocationOperation];
}
- (void)invocationOperationEvent{
    NSLog(@"%@",[NSThread currentThread]);
}
  • 使用NSInvocation傳遞多個參數(shù)
- (void)viewDidLoad {
    [super viewDidLoad];
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    NSMethodSignature * signature = [self methodSignatureForSelector:@selector(name:andAge:andSex:)];
    NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.selector = @selector(name:andAge:andSex:);
    invocation.target = self;
    NSString * name = @"Jerry";
    NSString * age = @"2";
    NSString * sex = @"男";
    //這里的index要從2開始滩届,前兩個已經(jīng)被target和selector占用
    [invocation setArgument:&name atIndex:2];
    [invocation setArgument:&age atIndex:3];
    [invocation setArgument:&sex atIndex:4];
    NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithInvocation:invocation];
    [queue addOperation:invocationOperation];
}
- (void)name:(NSString *)name andAge:(NSString *)age andSex:(NSString *)sex{
    NSLog(@"name = %@,age = %@,sex = %@",name,age,sex);
}

3.3 自定義Operation對象

3.3.1 Operation狀態(tài)(KVO通知)

  • 當(dāng)isFinished狀態(tài)為NO時,NSOperation不能執(zhí)行dealloc被啼。
@interface CustomOperation : NSOperation
@end
@implementation CustomOperation
@synthesize finished = _finished;
- (void)dealloc{
    NSLog(@"%s",__func__);  
}
- (BOOL)isFinished{
    return _finished;
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    NSOperationQueue * queue = [NSOperationQueue new];
    CustomOperation * customOperation = [CustomOperation new];
    [queue addOperation:customOperation];
}

CustomOperation不能執(zhí)行dealloc,用KVO的形式帜消,改變isFinished的值為YES,才能執(zhí)行CustomOperation才能被釋放。

        [self willChangeValueForKey:@"isFinished"];//_finished,finished
        _finished = YES;
        [self didChangeValueForKey:@"isFinished"];

3.3.2 配置Operation執(zhí)行行為(依賴關(guān)系泡挺,優(yōu)先級等)

    NSOperationQueue * operationQueue = [[NSOperationQueue alloc] init];
    NSBlockOperation * blockOperation = [[NSBlockOperation alloc] init];
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperation = %@",[NSThread currentThread]);
    }];
    NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationEvent) object:nil];
    
    NSBlockOperation * blockOperation2 = [[NSBlockOperation alloc] init];
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperation2 = %@",[NSThread currentThread]);
    }];
    //在入隊前處理好依賴關(guān)系
    [blockOperation addDependency:invocationOperation];
    [blockOperation2 addDependency:blockOperation];
    
    [operationQueue addOperation:blockOperation];
    [operationQueue addOperation:invocationOperation];
    [operationQueue addOperation:blockOperation2];

結(jié)果:

invocationOperation = <NSThread: 0x6080000740c0>{number = 3, name = (null)}
NSBlockOperation = <NSThread: 0x600000079580>{number = 4, name = (null)}
NSBlockOperation2 = <NSThread: 0x6080000740c0>{number = 3, name = (null)}

??:A線程依賴B線程,B依賴C命浴,C依賴A娄猫,這樣會產(chǎn)生死鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末生闲,一起剝皮案震驚了整個濱河市媳溺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碍讯,老刑警劉巖悬蔽,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捉兴,居然都是意外死亡屯阀,警方通過查閱死者的電腦和手機(jī)缅帘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來难衰,“玉大人钦无,你說我怎么就攤上這事「窍” “怎么了失暂?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鳄虱。 經(jīng)常有香客問我弟塞,道長,這世上最難降的妖魔是什么拙已? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任决记,我火速辦了婚禮,結(jié)果婚禮上倍踪,老公的妹妹穿的比我還像新娘系宫。我一直安慰自己,他們只是感情好建车,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布扩借。 她就那樣靜靜地躺著,像睡著了一般缤至。 火紅的嫁衣襯著肌膚如雪潮罪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天领斥,我揣著相機(jī)與錄音嫉到,去河邊找鬼。 笑死月洛,一個胖子當(dāng)著我的面吹牛何恶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播膊存,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼导而,長吁一口氣:“原來是場噩夢啊……” “哼忱叭!你這毒婦竟也來了隔崎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤韵丑,失蹤者是張志新(化名)和其女友劉穎爵卒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撵彻,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡钓株,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年实牡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轴合。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡创坞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出受葛,到底是詐尸還是另有隱情题涨,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布总滩,位于F島的核電站纲堵,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏闰渔。R本人自食惡果不足惜席函,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冈涧。 院中可真熱鬧茂附,春花似錦、人聲如沸炕舵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咽筋。三九已至溶推,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奸攻,已是汗流浹背蒜危。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留睹耐,地道東北人辐赞。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像硝训,于是被迫代替她去往敵國和親响委。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 首先了解單線程:一窖梁、單線程的應(yīng)用,整個應(yīng)用中只有一個順序執(zhí)行流,當(dāng)執(zhí)行流在執(zhí)行某個耗時的操作,或者不能立即完成的任...
    藍(lán)白七七閱讀 575評論 1 8
  • 由于前段時間赘风,堂弟的詢問,再加上自己也想重新整理一下知識結(jié)構(gòu)纵刘,就梳理一下知識邀窃。一系列的文章,可能會很多假哎,也可能會很...
    攝影師諾風(fēng)閱讀 675評論 5 15
  • 簡介 1. 線程分類 主線程(UI線程) : 處理和界面相關(guān)的事情. 子線程 : 處理耗時操作. Android中...
    王世軍Steven閱讀 917評論 0 2
  • 線程概述 線程與進(jìn)程 進(jìn)程 ?每個運(yùn)行中的任務(wù)(通常是程序)就是一個進(jìn)程瞬捕。當(dāng)一個程序進(jìn)入內(nèi)存運(yùn)行時鞍历,即變成了一個進(jìn)...
    閩越布衣閱讀 1,010評論 1 7
  • 時光匆匆, 溜過我的指縫肪虎。 盡管未來迷茫劣砍, 但是每天都有所不同。 你說這個冬天很冷扇救, 冷到凍僵了2016的夢秆剪。 我...