iOS 多線程處理 ----NSThread, NSOperation,GCD 2019-06-26

一個(gè)進(jìn)程要想執(zhí)行任務(wù)挣惰,必須得有線程(每1個(gè)進(jìn)程至少要有1條線程)     

線程 : 執(zhí)行任務(wù)的單元片段叫做線程,也就是真正的任務(wù)執(zhí)行者,只不過系統(tǒng)默認(rèn)把任務(wù)交給主線程來做. 大多時(shí)候?yàn)榱颂岣哂脩趔w驗(yàn)需要把耗時(shí)的任務(wù)交給子線程 來做.

一個(gè)進(jìn)程是由一個(gè)或多個(gè)線程組成.進(jìn)程只負(fù)責(zé)資源的調(diào)度和分配.線程才是 程序的執(zhí)行單元,負(fù)責(zé)代碼的執(zhí)行. 每個(gè)正在運(yùn)行的程序至少包含一個(gè)線程(即主線程),該線程在程序啟動(dòng)時(shí)被創(chuàng)建用于執(zhí)行mian函數(shù)
在多線程方法中為保證對(duì)象的即使釋放,需要為每個(gè)方法手動(dòng)添加自動(dòng)釋放池
iOS中關(guān)于UI的添加和刷新必須在主線程中操作
//多線程

1. NSthread
    優(yōu)點(diǎn): NSThread 比其他兩個(gè)輕量級(jí)
    缺點(diǎn) : 要自己管理線程的生命周期,線程同步.線程同步對(duì)數(shù)據(jù)的加鎖會(huì)有系統(tǒng)開銷
2. NSOperation
    優(yōu)點(diǎn): 不需要關(guān)心線程管理,數(shù)據(jù)同步的事情可以把精力放在自己需要操作的地方
 3. GCD 
    //優(yōu)點(diǎn): 集合了替代 NSThread, NSOperationQueue,NSInvocationOperation等的高效強(qiáng)大技術(shù),使用更方便

一 . NSThread

①使用NSThread的 類方法 創(chuàng)建子線程
在viewDidLoad方法創(chuàng)建線程
 [NSThread detachNewThreadSelector:@selector(handleNetWorkRequestImage1) toTarget:self withObject:nil];
-(void) handleNetWorkRequestImage1 {
    @autoreleasepool {
        NSURL *url = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/h%3D360/sign=4f75daaec35c10383b7ec8c48210931c/2cf5e0fe9925bc31fa45db2c5bdf8db1cb13706e.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
         UIImage *image = [UIImage imageWithData:data];
        //主線程跳轉(zhuǎn)到子線程執(zhí)行任務(wù)時(shí),會(huì)直接創(chuàng)建子線程,執(zhí)行耗時(shí)操作時(shí)直接使用該方法即可
        //當(dāng)子線程執(zhí)行完任務(wù)后,接下來的界面刷新操作等應(yīng)交由主線程操作,使用performSelectorOnMainThread:方法操作
        //從子線程回到主線程執(zhí)行任務(wù)
        [self performSelectorOnMainThread:@selector(refreashUIFirst:) withObject:image waitUntilDone:YES];
    }
}
-(void)refreashUIFirst:(UIImage *)image{
    self.imageShowFirst.image = image;
}
//② 使用NSThread 對(duì)象的--alloc-- init 初始化方法創(chuàng)建子線程
[[[NSThread alloc] initWithTarget:self selector:@selector(handleNetWorkImageRequest2) object:nil] start]
-(void) handleNetWorkImageRequest2{
    @autoreleasepool {
        NSURL *url = [NSURL URLWithString:@"http://images.enet.com.cn/egames/articleimage/201112/20111208025418685.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
         UIImage *image = [UIImage imageWithData:data];
      //返回主線程刷新界面
      [self performSelectorOnMainThread:@selector(refreashUISecond:) withObject:image waitUntilDone:YES];
    }
}
//刷新界面
-(void)refreashUISecond:(UIImage *)image{
    self.imageShowSecond.image = image;
}

??注意 : 初始化方法創(chuàng)建子線程時(shí)要通過手動(dòng)開啟 start 和取消 cancel 子線程或者結(jié)束線程[NSThread exit]

線程互斥是指某一資源同時(shí)只允許一個(gè)訪問者對(duì)其進(jìn)行訪問仍侥,具有唯一性和排它性调榄。但互斥無法限制訪問者對(duì)資源的訪問順序瓢对,即訪問是無序的

當(dāng)多個(gè)線程同時(shí)訪問一個(gè)資源時(shí)會(huì)產(chǎn)生線程互斥的問題,如何解決NSThread的線程同步互斥問題呢?
(1)加鎖NSLock 或NSCondition (2)使用@Synchronized
****************賣票問題 解決NSThread的線程同步互斥問題********************

創(chuàng)建票數(shù)屬性 totalTickets , 在viewDidLoad方法中添加兩個(gè)線程

    totalTickets = 100;//初始化票的總張數(shù)
    self.lock = [[NSLock alloc] init];
    //窗口1
    [NSThread detachNewThreadSelector:@selector(sellTicketsWithName:) toTarget:self withObject:@"張三"];
    //窗口2
    [NSThread detachNewThreadSelector:@selector(sellTicketsWithName:) toTarget:self withObject:@"李四"];
     -(void)sellTicketsWithName:(NSString *)name{
    @autoreleasepool {
        while (YES) {
            [self.lock lock];//線程加鎖
            if (totalTickets > 0) { //賣票
                [NSThread sleepForTimeInterval:0.09];
                totalTickets --;
                NSLog(@"%@賣的票,剩余%ld張" ,name,totalTickets);
            }else{//沒票
                NSLog(@"%@ 票買完了",name);
                break;
            }
            [self.lock unlock];//解鎖
            //線程死鎖:臨界資源缺少解鎖,就會(huì)造成死鎖,其他線程一致等待前一個(gè)線程解鎖
    
           /*
            @synchronized(name) {
                if (totalTickets > 0) {
                    //賣票
                    [NSThread sleepForTimeInterval:0.09];
                    totalTickets --;
                    NSLog(@"%@賣的票,剩余%ld張" ,name,totalTickets);
                }else{
                    //沒票
                    NSLog(@"%@ 票買完了",name);
                    break;
                }
            }
          */
        }
    }
}
       

二 .NSObject

//創(chuàng)建 異步后臺(tái)執(zhí)行 子線程,使用NSobject提供的方法

[self performSelectorInBackground:@selector(handleImageRequest3) withObject:nil];
-(void) handleImageRequest3{
    @autoreleasepool {
        NSURL *url = [NSURL URLWithString:@"http://image.tianjimedia.com/uploadImages/2012/243/8RM0WDLRMWNA.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
         UIImage *image = [UIImage imageWithData:data];
 //從子線程回到主線程執(zhí)行任務(wù)
          [self performSelectorOnMainThread:@selector(refreashUIThird:) withObject:image waitUntilDone:YES];
    }
}

-(void)refreashUIThird:(UIImage *)image{
    self.imageShowThird.image = image;
}

三 .NSOperation

NSOperation 類在MVC中屬于M,是用來封裝單個(gè)任務(wù)相關(guān)的代碼和數(shù)據(jù)的抽象出來的類, 他只是一個(gè)操作沒有主線程,子線程之分,本身與多線程沒有任何關(guān)系, 通常不直接使用而是使用其子類(NSInvocationOperation或NSBlockOperation)

使用 操作隊(duì)列 NSOperationQueue來 管理一組 Operation對(duì)象 ,根據(jù)需要為 operation 開辟合適數(shù)量的線程 實(shí)現(xiàn)任務(wù)的并行執(zhí)行
 1.線程同步 : 同步執(zhí)行, 任務(wù)之間存在先后順序,后一任務(wù)在前一任務(wù)完成后才執(zhí)行
  /* 線程同步存在兩種方法:
    第一種 : 設(shè)置線程并發(fā)數(shù)
    第二種 : 設(shè)置多任務(wù)的依賴關(guān)系
  */   
 2.線程并發(fā) : 異步執(zhí)行(不需要設(shè)置并發(fā)數(shù)),任務(wù)之間沒有先后順序,先執(zhí)行的可能最后結(jié)束

(3.1) NSInvocationOperation 封裝了執(zhí)行操作的target和要執(zhí)行的action

 NSInvocationOperation *operation1 =  [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleNetWorkRequestImage1) object:nil];
    NSInvocationOperation *operation2 =  [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleNetWorkImageRequest2) object:nil];
    //創(chuàng)建任務(wù)隊(duì)列,來操作任務(wù)的執(zhí)行
        //① 設(shè)置線程并發(fā)數(shù)為1 ,實(shí)現(xiàn)同步執(zhí)行
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue setMaxConcurrentOperationCount:1];
    [queue addOperation:operation1];
    [queue addOperation:operation2];

(3.2) NSBlockOperation 封裝了要執(zhí)行的代碼塊

NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{
        [self handleImageRequest3];
    }];
    NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
        [self handleImageRequestFromNet4];
    }];

//網(wǎng)絡(luò)請(qǐng)求圖片
-(void) handleImageRequest3{
    @autoreleasepool {
        NSURL *url = [NSURL URLWithString:@"http://image.tianjimedia.com/uploadImages/2012/243/8RM0WDLRMWNA.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
         UIImage *image = [UIImage imageWithData:data];
        //從子線程回到主線程執(zhí)行任務(wù)
          [self performSelectorOnMainThread:@selector(refreashUIThird:) withObject:image waitUntilDone:YES];
    }
}
-(void) handleImageRequestFromNet4{
    @autoreleasepool {
        NSURL *url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/h%3D200/sign=a4d0f7d46409c93d18f209f7af3ff8bb/024f78f0f736afc314f682bfb019ebc4b6451275.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
          [self performSelectorOnMainThread:@selector(refreashUIFourth:) withObject:image waitUntilDone:YES];
    }
}
//刷新界面
-(void)refreashUIThird:(UIImage *)image{
    self.imageShowThird.image = image;
}
-(void)refreashUIFourth:(UIImage *)image{
    self.imageShowFourth.image = image;
}

四 .GCD處理多線程

GCD的工作原理是:讓程序平行排隊(duì)的特定任務(wù)忙芒,根據(jù)可用的處理資源鸣戴,安排他們?cè)谌魏慰捎玫奶幚砥骱诵纳蠄?zhí)行任務(wù)档桃。任務(wù)可以是一個(gè)函數(shù)(function)或者是一個(gè)block。 GCD的底層依然是用線程實(shí)現(xiàn)蝶防,不過這樣可以讓程序員不用關(guān)注實(shí)現(xiàn)的細(xì)節(jié)甚侣。 
GCD完全可以處理諸如 數(shù)據(jù)鎖定和資源泄漏等復(fù)雜的異步編程問題 
//串行隊(duì)列
- (IBAction)chuanXing:(UIButton *)sender {
//1.創(chuàng)建串行隊(duì)列
    //(1)獲取系統(tǒng)創(chuàng)建好的串行隊(duì)列,在主線程中實(shí)現(xiàn)線程同步
    dispatch_queue_t queue1 = dispatch_get_main_queue();
    //(2)自己創(chuàng)建串行隊(duì)列,任務(wù)在子線程是線程同步
    //參數(shù)一 : 隊(duì)列名稱 也是隊(duì)列的唯一標(biāo)示,蘋果建議采用反域名形式編寫
    //參數(shù)二 : 指定為什么類型的隊(duì)列
    //DISPATCH_QUEUE_SERIAL ------指定為串行隊(duì)列
    dispatch_queue_t  queue2 = dispatch_queue_create("com.lanou3g.www", DISPATCH_QUEUE_SERIAL);
//2.往隊(duì)列中添加任務(wù)
    dispatch_async(queue2, ^{
        NSLog(@"任務(wù)一%@",[NSThread currentThread]);
    });
    dispatch_async(queue2, ^{
        NSLog(@"任務(wù)二%@",[NSThread currentThread]);
    });
    dispatch_async(queue2, ^{
        NSLog(@"任務(wù)三%@",[NSThread currentThread]);
    });
    dispatch_async(queue2, ^{
        NSLog(@"任務(wù)四%@",[NSThread currentThread]);
    });
    //釋放 ------MRC
//    dispatch_release(queue2);

}
//并行隊(duì)列
- (IBAction)bingXing:(UIButton*)sender {
//1.創(chuàng)建并行隊(duì)列
    //(1)使用系統(tǒng)創(chuàng)建好的并行隊(duì)列
    //參數(shù)一 : 優(yōu)先級(jí) 系統(tǒng)提供四種
    //參數(shù)二 : 預(yù)留參數(shù) 現(xiàn)在未使用 給 0 即可
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //(2)自己創(chuàng)建并發(fā)隊(duì)列
    dispatch_queue_t  queue3 = dispatch_queue_create("com.lanou3g.www", DISPATCH_QUEUE_CONCURRENT);
    //往隊(duì)列中添加任務(wù)
    dispatch_async(queue3, ^{
        NSLog(@"任務(wù)一%@",[NSThread currentThread]);
    });
    dispatch_async(queue3, ^{
        NSLog(@"任務(wù)二%@",[NSThread currentThread]);
    });
    dispatch_async(queue3, ^{
        NSLog(@"任務(wù)三%@",[NSThread currentThread]);
    });
    dispatch_async(queue3, ^{
        NSLog(@"任務(wù)四%@",[NSThread currentThread]);
        //請(qǐng)求到數(shù)據(jù)后要回到主線程刷新界面
        dispatch_async(dispatch_get_main_queue(), ^{
            //此處寫想在主線程中執(zhí)行的代碼段  --- 比如:刷新UI界面等操作
        });
    });
    //MRC 時(shí)釋放
    // dispatch_release(queue3);

}
//分組隊(duì)列

- (IBAction)fenZu:(UIButton *)sender {
//1.創(chuàng)建并行隊(duì)列
    dispatch_queue_t queue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //2. 創(chuàng)建分組
    dispatch_group_t group = dispatch_group_create();
    //3.往分組隊(duì)列添加任務(wù)
    dispatch_group_async(group, queue4, ^{
        NSLog(@"任務(wù)一  ,請(qǐng)求0-10M的數(shù)據(jù)");
    });
    dispatch_group_async(group, queue4, ^{
        NSLog(@"任務(wù)二 ,請(qǐng)求10-20M的數(shù)據(jù)");
    });
    dispatch_group_async(group, queue4, ^{
        NSLog(@"任務(wù)三 ,請(qǐng)求20-30M的數(shù)據(jù)");
    });
    dispatch_group_async(group, queue4, ^{
        NSLog(@"任務(wù)四 ,請(qǐng)求20-40M的數(shù)據(jù)");
    });
    //當(dāng)分組所有任務(wù)完成后出發(fā)的方法
    dispatch_group_notify(group, queue4, ^{
        //數(shù)據(jù)拼接
        NSLog(@"數(shù)據(jù)拼接");
    });
    //MRC 時(shí)釋放
    // dispatch_release(group);
}
//一次
- (IBAction)Once:(UIButton *)sender {

}

//障礙隊(duì)列
- (IBAction)zhangAi:(UIButton *)sender {
//障礙任務(wù)的作用 : 可以保證障礙之后的并發(fā)的任務(wù)在障礙之后并發(fā)的任務(wù)執(zhí)行完畢之后去執(zhí)行
    //注意: 如果添加障礙任務(wù)必須使用自己創(chuàng)建的并發(fā)隊(duì)列
    //1.創(chuàng)建并發(fā)隊(duì)列
    dispatch_queue_t quee = dispatch_queue_create("com.lanou.henan", DISPATCH_QUEUE_CONCURRENT);
    //2.往隊(duì)列中添加任務(wù)
    dispatch_async(quee, ^{
        NSLog(@" A 寫入");
    });
    dispatch_async(quee, ^{
        NSLog(@" B 寫入");
    });
    dispatch_async(quee, ^{
        NSLog(@" C 寫入");
    });
    dispatch_async(quee, ^{
        NSLog(@" D 寫入");
    });
    //添加障礙任務(wù)
    dispatch_barrier_async(quee, ^{
        NSLog(@"此處是坑,障礙");
    });
    
    dispatch_async(quee, ^{
        NSLog(@" A 讀取");
    });
    dispatch_async(quee, ^{
        NSLog(@" B 讀取");
    });
    dispatch_async(quee, ^{
        NSLog(@" C 讀取");
    });
    dispatch_async(quee, ^{
        NSLog(@" D 讀取");
    });
    //MRC ---釋放
    //dispatch_release(quee);
}

//延遲
- (IBAction)yanChi:(UIButton *)sender {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"蠻王好持久啊啊啊!");
    });
    //dispatch_get_main_queue()在主線程中執(zhí)行  如果想在子線程執(zhí)行, 此處改為子線程即可
}
//重復(fù)執(zhí)行
- (IBAction)chongFu:(UIButton *)sender {
    dispatch_queue_t quuee = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_apply(10, quuee, ^(size_t index) {
        NSLog(@"反復(fù)執(zhí)行的次數(shù) %ld , 當(dāng)前線程 %@",index,[NSThread currentThread]);
   //注意: size_t之后手寫上參數(shù)名 比如 index
        //重復(fù)的任務(wù)在執(zhí)行的過程中至少一次是在主隊(duì)列中
    });
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市间学,隨后出現(xiàn)的幾起案子殷费,更是在濱河造成了極大的恐慌,老刑警劉巖低葫,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件详羡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡嘿悬,警方通過查閱死者的電腦和手機(jī)实柠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來善涨,“玉大人窒盐,你說我怎么就攤上這事「峙。” “怎么了登钥?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長娶靡。 經(jīng)常有香客問我牧牢,道長,這世上最難降的妖魔是什么姿锭? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任塔鳍,我火速辦了婚禮,結(jié)果婚禮上呻此,老公的妹妹穿的比我還像新娘轮纫。我一直安慰自己,他們只是感情好焚鲜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布掌唾。 她就那樣靜靜地躺著,像睡著了一般忿磅。 火紅的嫁衣襯著肌膚如雪糯彬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天葱她,我揣著相機(jī)與錄音撩扒,去河邊找鬼。 笑死吨些,一個(gè)胖子當(dāng)著我的面吹牛搓谆,可吹牛的內(nèi)容都是我干的炒辉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼泉手,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼黔寇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起斩萌,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤啡氢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后术裸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亭枷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年袭艺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叨粘。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猾编,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出升敲,到底是詐尸還是另有隱情答倡,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布驴党,位于F島的核電站瘪撇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏港庄。R本人自食惡果不足惜倔既,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鹏氧。 院中可真熱鬧渤涌,春花似錦、人聲如沸把还。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吊履。三九已至安皱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間艇炎,已是汗流浹背练俐。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冕臭,地道東北人腺晾。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓燕锥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親悯蝉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子归形,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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