iOS 多線程系列 -- NSThread

iOS 多線程系列 -- 基礎(chǔ)概述
iOS 多線程系列 -- pthread
iOS 多線程系列 -- NSThread
iOS 多線程系列 -- GCD全解一(基礎(chǔ))
iOS 多線程系列 -- GCD全解二(常用方法)
iOS 多線程系列 -- GCD全解三(進(jìn)階)
iOS 多線程系列 -- NSOperation
測試Demo的GitHub地址

1.NSThread基礎(chǔ)

  • 一個NSThread對象就代表一條線程
  • 創(chuàng)建、啟動線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];// 啟動線程蟋滴,在線程thread中執(zhí)行self的run方法
  • 主線程相關(guān)用法
+ (NSThread *)mainThread; // 獲得主線程
- (BOOL)isMainThread; // 是否為主線程
+ (BOOL)isMainThread; // 是否為主線程
  • 獲得當(dāng)前線程
NSThread *current = [NSThread currentThread];
  • 線程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;

//示例:
thread.name = @"my-thread";

  • 其他創(chuàng)建線程方式
  • 這2種創(chuàng)建線程方式的優(yōu)缺點
    優(yōu)點:簡單快捷
    缺點:無法對線程進(jìn)行更詳細(xì)的設(shè)置
//創(chuàng)建線程后自動啟動線程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"XL"];
//隱式創(chuàng)建并啟動線程
[self performSelectorInBackground:@selector(run:) withObject:@"OC"];

2 控制線程狀態(tài)的方法有:

2.1 啟動線程

啟動線程
- (void)start;
// 進(jìn)入就緒狀態(tài) -> 運行狀態(tài)城舞。當(dāng)線程任務(wù)執(zhí)行完畢广凸,自動進(jìn)入死亡狀態(tài)
[thread start];

2.2 阻塞(暫停)線程

+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 調(diào)用這個方法的線程進(jìn)入阻塞狀態(tài)澄成,
[NSThread sleepForTimeInterval:2];
//第二種線程暫停睡眠的方法
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
//遙遠(yuǎn)的未來才開始運行鲜棠,相當(dāng)于一直停止
[NSThread sleepUntilDate:[NSDate distantFuture]];

2.3 強制停止線程 ,

  • 調(diào)用之后會立即終止線程暖哨,即使任務(wù)還沒有執(zhí)行完成也會中斷.不推薦使用此方式退出子線程,可能會造成內(nèi)存泄漏
+ (void)exit;// 這是一個類方法,調(diào)用這個方法的所在線程會進(jìn)入死亡狀態(tài)
[NSThread exit];
  • 取消線程
    • NSThread提供了一個cancel方法,和一個cancelled屬性
    • cancel方法只是讓isCancelled屬性值置為YES,修改了線程狀態(tài),并不會停止/退出線程,任務(wù)會繼續(xù)執(zhí)行除非你做了一些其他操作
    • 我們可以監(jiān)聽isCancelled值的變化,做出相應(yīng)的調(diào)整,如下面示例代碼中,在子線程任務(wù)中監(jiān)聽cancel狀態(tài),如果cancel == yes , 回收內(nèi)存等資源,然后return或者調(diào)用exit退出線程,隨后線程就會銷毀
   
- (void)createThreadCancel
{
    XLThread *thread = [[XLThread alloc] initWithTarget:self selector:@selector(runCancel) object:@"XL"];
    thread.name = @"cancel-thread";
    [thread start];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"before thread.isCancelled = %zd",thread.isCancelled);
        [thread cancel];
        NSLog(@"after thread.isCancelled = %zd",thread.isCancelled);
    });
}

- (void)runCancel
{
    for (NSInteger i = 0; i<10000; i++) {
        NSLog(@"-----%zd , [NSThread currentThread] = %@", i , [NSThread currentThread]);
        sleep(0.01);
        if ([NSThread currentThread].isCancelled) {
            // 進(jìn)行線程退出前的一些操作,如內(nèi)存回收等
            [NSThread exit]; // 線程退出,其后面的代碼不會被執(zhí)行 , 或者調(diào)用return;
            NSLog(@"-----exit [NSThread currentThread] = %@" , [NSThread currentThread]);
        }
    }
}

3 多線程的安全隱患

  • 解決方法:互斥鎖
  • 互斥鎖使用格式
  • @synchronized(鎖對象) { // 需要鎖定的代碼 }
  • 注意:鎖定1份代碼只用1把鎖赌朋,用多把鎖是無效的,必須是同一個變量,雖然這個變量可以是任意類型的
    - (void)saleTicket
    {
        while (1) {
            @synchronized(self) {
                // 先取出總數(shù)
                NSInteger count = self.ticketCount;
                if (count > 0) {
                    self.ticketCount = count - 1;
                    NSLog(@"%@賣了一張票,還剩下%zd張", [NSThread currentThread].name, self.ticketCount);
                } else {
                    NSLog(@"票已經(jīng)賣完了");
                    break;
                }
            }
        }
    }
  • 互斥鎖的優(yōu)缺點

  • 優(yōu)點:能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題

  • 缺點:需要消耗大量的CPU資源

  • 互斥鎖的使用前提:多條線程搶奪同一塊資源沛慢,如果沒有搶奪資源赡若,就不要加鎖

  • 相關(guān)專業(yè)術(shù)語:線程同步

  • 線程同步的意思是:多條線程在同一條線上執(zhí)行(按順序地執(zhí)行任務(wù))

  • 互斥鎖,就是使用了線程同步技術(shù)

  • OC在定義屬性時有nonatomic和atomic兩種選擇

  • atomic:原子屬性团甲,為setter方法加鎖(默認(rèn)就是atomic),不能保證線程數(shù)據(jù)安全,他只是這個setter方法加鎖,如果 setter方法
    內(nèi)部還包含別的對象的setter方法,就不會限制,不能保證安全

  • nonatomic:非原子屬性逾冬,不會為setter方法加鎖

  • nonatomic和atomic對比

  • atomic:線程安全,需要消耗大量的資源

  • nonatomic:非線程安全伐庭,適合內(nèi)存小的移動設(shè)備

4 線程間通信

  • 什么叫做線程間通信
  • 在1個進(jìn)程中粉渠,線程往往不是孤立存在的,多個線程之間需要經(jīng)常進(jìn)行通信
  • 線程間通信的體現(xiàn)
  • 1個線程傳遞數(shù)據(jù)給另1個線程
  • 在1個線程中執(zhí)行完特定任務(wù)后圾另,轉(zhuǎn)到另1個線程繼續(xù)執(zhí)行任務(wù)
  • 線程間通信常用方法
// 方法1
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
// 方法2
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
// 方法3
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0);
// 方法4
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
// equivalent to the first method with kCFRunLoopCommonModes
  • 上述幾個方法都是讓某個線程去執(zhí)行某個方法霸株,參數(shù)解析:
  • waitUntilDone : 是否等待指定線程執(zhí)行完任務(wù)。YES表示當(dāng)前線程會被阻塞等待制定線程任務(wù)執(zhí)行完畢集乔;NO表示不等待去件,
  • modes: 表示指定線程runloop的執(zhí)行模式
  • withObject : 攜帶的參數(shù)
  • onThread : 執(zhí)行任務(wù)的線程,注意: 這個線程必須有runloop扰路,否則不會執(zhí)行指定的aSelector 任務(wù)尤溜,如果這個線程沒有runloop,調(diào)用這些方法時waitUntilDone設(shè)置為YES會閃退汗唱,waitUntilDone設(shè)置為NO時線程會銷毀宫莱,看demo中的performTest測試結(jié)果
  • 使用示例(子線程下載加載圖片):
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self performSelectorInBackground:@selector(download3) withObject:nil];
}
- (void)download3
{
    NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    // 回到主線程,顯示圖片(下面三個方法都一樣)
    [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
    //    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
    //    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
}
- (void)showImage:(UIImage *)image
{
    self.imageView.image = image;
}

5. NSThread和NSRunloop結(jié)合使用 - 常駐線程

  • 上面說到?jīng)]有runloop的線程執(zhí)行完相應(yīng)的任務(wù)就會結(jié)束哩罪,這時用performSelector系列方法讓指定線程繼續(xù)執(zhí)行任務(wù)是不可行的授霸,如何讓我們創(chuàng)建的線程持有一個runloop
- (void)runPerformWithRunloop {
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];//RunLoop中沒有事件源、定時器等际插,進(jìn)入RunLoop后就會立即推出RunLoop碘耳,留不住線程 , 所以必須添加一個port源
    while (![[NSThread currentThread] isCancelled]) {// 檢查線程的isCancelled狀態(tài)框弛,如果線程狀態(tài)被設(shè)置為canceled辛辨,就退出線程,否則就繼續(xù)進(jìn)入runloop瑟枫,10s后退出runloop重新判斷線程的最新狀態(tài)
        [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];  //⑤啟動runloop 斗搞,10s后退出runloop
    }
}
注意點:
  • 子線程的runloop在獲取時創(chuàng)建
  • RunLoop中如果沒有事件源、定時器等慷妙,進(jìn)入RunLoop后就會立即推出RunLoop僻焚,留不住線程 , 所以必須
    添加一個port源
  • runloop啟動方法有三個景殷,這里推薦runMode:beforeDate: ( 前兩個方法實現(xiàn)的常駐線程無法正常退出,只能強制調(diào)用exit退出線程)
- (void)run;
- (void)runUntilDate:(NSDate *)limitDate;
- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
  • 更詳細(xì)的runloop知識溅呢,可以看以后的runloop章節(jié)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末澡屡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子咐旧,更是在濱河造成了極大的恐慌驶鹉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铣墨,死亡現(xiàn)場離奇詭異室埋,居然都是意外死亡,警方通過查閱死者的電腦和手機伊约,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門姚淆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人屡律,你說我怎么就攤上這事腌逢。” “怎么了超埋?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵搏讶,是天一觀的道長。 經(jīng)常有香客問我霍殴,道長媒惕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任来庭,我火速辦了婚禮妒蔚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘月弛。我一直安慰自己肴盏,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布尊搬。 她就那樣靜靜地躺著叁鉴,像睡著了一般土涝。 火紅的嫁衣襯著肌膚如雪佛寿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天但壮,我揣著相機與錄音冀泻,去河邊找鬼。 笑死蜡饵,一個胖子當(dāng)著我的面吹牛弹渔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播溯祸,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼肢专,長吁一口氣:“原來是場噩夢啊……” “哼舞肆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起博杖,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤椿胯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后剃根,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哩盲,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年狈醉,在試婚紗的時候發(fā)現(xiàn)自己被綠了廉油。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡苗傅,死狀恐怖抒线,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情渣慕,我是刑警寧澤十兢,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站摇庙,受9級特大地震影響旱物,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卫袒,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一宵呛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧夕凝,春花似錦宝穗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至转砖,卻和暖如春须鼎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背府蔗。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工晋控, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人姓赤。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓赡译,卻偏偏與公主長得像,于是被迫代替她去往敵國和親不铆。 傳聞我的和親對象是個殘疾皇子蝌焚,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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

  • 多線程基本概念 單核CPU裹唆,同一時間cpu只能處理1個線程,只有1個線程在執(zhí)行 。多線程同時執(zhí)行:是CPU快速的在...
    WeiHing閱讀 699評論 1 5
  • .一.進(jìn)程 進(jìn)程:是指在系統(tǒng)中正在運行的一個應(yīng)用程序,每個進(jìn)程之間是獨立的只洒,每個進(jìn)程均運行在其專用且受保護的內(nèi)存空...
    IIronMan閱讀 4,476評論 1 33
  • 一品腹、多線程基礎(chǔ) 基本概念 進(jìn)程進(jìn)程是指在系統(tǒng)中正在運行的一個應(yīng)用程序每個進(jìn)程之間是獨立的,每個進(jìn)程均運行在其專用且...
    AlanGe閱讀 542評論 0 0
  • Object C中創(chuàng)建線程的方法是什么红碑?如果在主線程中執(zhí)行代碼舞吭,方法是什么?如果想延時執(zhí)行代碼析珊、方法又是什么羡鸥? 1...
    AlanGe閱讀 1,716評論 0 17
  • 很多年前的這個季節(jié),我和同學(xué)大烏鴉閑極無聊之際同游玄武湖忠寻,從解放門進(jìn)去惧浴,直接去劃船。 當(dāng)時的玄武湖還是收10元門票...
    花開遍野閱讀 213評論 0 3