iOS -- 多線程開發(fā)

多線程開發(fā)

1.NSThread

2.NSOperation

3.GCD

三種方式是隨著iOS的發(fā)展逐漸引入的,所以相比而言后者比前者更加簡單易用,并且GCD也是目前蘋果官方比較推薦的方式(它充分利用了多核處理器的運算性能)。
在iOS中每個進程啟動后都會建立一個主線程(UI線程)雹食,這個線程是其他線程的父線程。由于在iOS中除了主線程颠猴,其他子線程是獨立于Cocoa Touch的棵帽,所以只有主線程可以更新UI界面.

NSThread

    // 方法1.  對象方法  (手動開啟)  
    // (1)創(chuàng)建線程 通铲,把復雜任務放到子線程中執(zhí)行  
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(blockThread:) object:nil];  
    // (2) 手動開啟  
    [thread start];
       
   // 子線程    
   - (IBAction)blockThread:(id)sender {  
     @autoreleasepool {
        // 凡是子線程執(zhí)行的方法,都添加到 @autoreleasepool{} 自動釋放池
      } 
    }  
  // 方法2.  類方法   (無需手動開啟)  
  // 方法2. 創(chuàng)建線程片任,把復雜任務放到子線程中執(zhí)行  
  [NSThread detachNewThreadSelector:@selector(blockThread:) toTarget:self withObject:nil];
NSThread總結(jié):

1.每個線程的實際執(zhí)行順序并不一定按順序執(zhí)行(雖然是按順序啟動)偏友;

2.每個線程執(zhí)行時實際網(wǎng)絡狀況很可能不一致。當然網(wǎng)絡問題無法改變对供,只能盡可能讓網(wǎng)速更快位他,但是可以改變線程的優(yōu)先級,讓15個線程優(yōu)先執(zhí)行某個線程。線程優(yōu)先級范圍為0~1,值越大優(yōu)先級越高陷寝,每個線程的優(yōu)先級默認為0.5。

3.優(yōu)先級高的執(zhí)行窿冯,只是說,執(zhí)行的概率變高醋粟,并不是最先執(zhí)行靡菇。

4.使用NSThread在進行多線程開發(fā)過程中操作比較簡單重归,但是要控制線程執(zhí)行順序并不容易米愿,另外在這個過程中如果打印線程會發(fā)現(xiàn)循環(huán)幾次就創(chuàng)建了幾個線程,這在實際開發(fā)過程中是不得不考慮的問題鼻吮,因為每個線程的創(chuàng)建也是相當占用系統(tǒng)開銷的育苟。

線程狀態(tài)

線程狀態(tài)分為isExecuting(正在執(zhí)行)isFinished(已經(jīng)完成)椎木、isCancellled(已經(jīng)取消)三種违柏。其中取消狀態(tài)程序可以干預設置,只要調(diào)用線程的cancel方法即可香椎。但是需要注意在主線程中僅僅能設置線程狀態(tài)漱竖,并不能真正停止當前線程,如果要終止線程必須在線程中調(diào)用exist方法畜伐,這是一個靜態(tài)方法馍惹,調(diào)用該方法可以退出當前線程。

在線程操作過程中可以讓某個線程休眠等待玛界,優(yōu)先執(zhí)行其他線程操作万矾,而且在這個過程中還可以修改某個線程的狀態(tài)或者終止某個指定線程。

-(NSData *)requestData:(int )index{ 
//對于多線程操作建議把線程操作放到@autoreleasepool中
 @autoreleasepool{ //對一加載線程休眠2秒 
if (index!=(ROW_COUNT*COLUMN_COUNT-1)) {
 [NSThread sleepForTimeInterval:2.0]; 
 }
 NSURL *url=[NSURL URLWithString:_imageNames[index]]; 
 NSData *data=[NSData dataWithContentsOfURL:url]; return data; 
 }
 }

NSOperation

使用NSOperation和NSOperationQueue進行多線程開發(fā)類似于C#中的線程池慎框,只要將一個NSOperation(實際開中需要使用其子類NSInvocationOperation良狈、NSBlockOperation)放到NSOperationQueue這個隊列中線程就會依次啟動。NSOperationQueue負責管理笨枯、執(zhí)行所有的NSOperation薪丁,在這個過程中可以更加容易的管理線程總數(shù)和控制線程之間的依賴關系遇西。

NSOperation有兩個常用子類用于創(chuàng)建線程操作:NSInvocationOperation和NSBlockOperation,兩種方式本質(zhì)沒有區(qū)別窥突,但是是后者使用Block形式進行代碼組織努溃,使用相對方便。

注意:操作本身只是封裝了要執(zhí)行的相關方法阻问,并沒有開辟線程梧税,沒有主線程之分,在哪個線程中都能執(zhí)行称近。

//  invocate 創(chuàng)建操作
//    NSInvocationOperation *invo = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(blockThread:) object:nil];
    // 開始任務
//    [invo start];
    
    // 1. 創(chuàng)建5個操作
    NSInvocationOperation *invo1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printString:) object:@"11"];
    NSInvocationOperation *invo2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printString:) object:@"22"];
    NSInvocationOperation *invo3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printString:) object:@"33"];
    NSInvocationOperation *invo4 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printString:) object:@"44"];
    NSInvocationOperation *invo5 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printString:) object:@"55"];
    
    // 2. NSBlockOperation 創(chuàng)建2個block操作
    NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"666666");
    }];
    NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"777777");
    }];
    
    
    // 3. 創(chuàng)建 操作隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 4. 設置最大并發(fā)數(shù)
    queue.maxConcurrentOperationCount = 1; // 設置為 1第队,順序執(zhí)行
    
    // 最大并發(fā)數(shù)控制的是同一時間點,能夠執(zhí)行的任務數(shù)刨秆。如果為1凳谦,則同時執(zhí)行1個,根據(jù)隊列FIFO特點衡未,一定是順序執(zhí)行尸执。 如果不為1,則可以同時執(zhí)行多個任務缓醋,同時執(zhí)行任務如失,稱為--并發(fā)執(zhí)行。
    
    // 5. 添加操作--將操作添加到隊列
//    [queue addOperation:invo];
    [queue addOperation:invo1];
    [queue addOperation:invo2];
    [queue addOperation:invo3];
    [queue addOperation:invo4];
    [queue addOperation:invo5];
    [queue addOperation:block1];
    [queue addOperation:block2];
    
    // 添加到隊列中的任務會自動執(zhí)行送粱,隊列內(nèi)部會開辟子線程褪贵,任務放在子線程中執(zhí)行。***********************************************
    
    //方法2:直接使用操隊列添加操作,eg:block2
    [queue addOperationWithBlock:^{
        NSLog(@"777777");
    }];
}
  • 使用NSBlockOperation方法抗俄,所有的操作不必單獨定義方法脆丁,同時解決了只能傳遞一個參數(shù)的問題。
  • 調(diào)用主線程隊列的 addOperationWithBlock: 方法進行UI更新动雹,不用再定義一個參數(shù)實體槽卫。
  • 使用NSOperation進行多線程開發(fā)可以設置最大并發(fā)線程,有效的對線程進行了控制胰蝠。

線程執(zhí)行順序

使用NSThread很難控制線程的執(zhí)行順序歼培,但是使用NSOperation就容易多了,每個NSOperation可以設置依賴線程姊氓。假設操作A依賴于操作B丐怯,線程操作隊列在啟動線程時就會首先執(zhí)行B操作,然后執(zhí)行A翔横。

// 加載5張圖片读跷,優(yōu)先加載最后一張圖的需求,只要設置前面的線程操作的依賴線程為最后一個操作即可禾唁。
-(void)loadImageWithMultiThread{
     int count=ROW_COUNT*COLUMN_COUNT; //創(chuàng)建操作隊列 
     NSOperationQueue   *operationQueue=[[NSOperationQueue alloc]init]; 
     operationQueue.maxConcurrentOperationCount=5;
     //設置最大并發(fā)線程數(shù) 
     NSBlockOperation   *lastBlockOperation=[NSBlockOperation blockOperationWithBlock:^{ 
         [self loadImage:[NSNumber numberWithInt:(count-1)]];
     }]; 
     //創(chuàng)建多個線程用于填充圖片
     for (int i=0; i<count-1; ++i) { 
         //方法1:創(chuàng)建操作塊添加到隊列 
         //創(chuàng)建多線程操作 
         NSBlockOperation *blockOperation=[NSBlockOperation blockOperationWithBlock:^{ 
            [self loadImage:[NSNumber numberWithInt:i]];
         }];
        //設置依賴操作為最后一張圖片加載操作
        [blockOperation addDependency:lastBlockOperation]; 
        [operationQueue addOperation:blockOperation]; 
      } 
      //將最后一個圖片的加載操作加入線程隊列 
      [operationQueue addOperation:lastBlockOperation]; 
}

加載最后一張圖片的操作最后被加入到操作隊列效览,但是它卻是被第一個執(zhí)行的无切。操作依賴關系可以設置多個,例如A依賴于B丐枉、B依賴于C…但是千萬不要設置為循環(huán)依賴關系(例如A依賴于B哆键,B依賴于C,C又依賴于A)瘦锹,否則是不會被執(zhí)行的籍嘹。

GCD

  • GCD(grand Center DisPath) 宏觀中心分配(隊列).
  • 是蘋果開發(fā)的一種支持并行操作的機制。它的主要部件是一個FIFO隊列和一個線程池弯院,前者用來添加任務辱士,后者用來執(zhí)行任務。
  • GCD中的FIFO隊列稱為dispatch queue听绳,它可以保證先進來的任務先得到執(zhí)行(但不保證一定先執(zhí)行結(jié)束)颂碘。
  • GCD 是一個函數(shù)級的多線程,用C語言實現(xiàn)的椅挣。GCD 中可以分配多個隊列头岔,每個隊列都具有一定的功能。比如:串行隊列鼠证,并發(fā)隊列峡竣,分組隊列,只執(zhí)行一次隊列名惩。

dispatch queue分為下面兩種:

    1. Serial Dispatch Queue -- 線程池只提供一個線程用來執(zhí)行任務澎胡,所以后一個任務必須等到前一個任務執(zhí)行結(jié)束才能開始孕荠。
    1. Concurrent Dispatch Queue -- 線程池提供多個線程來執(zhí)行任務娩鹉,所以可以按序啟動多個任務并發(fā)執(zhí)行。
// 1. 創(chuàng)建一個串行隊列
    dispatch_queue_t serialQ = dispatch_queue_create("q1", DISPATCH_QUEUE_SERIAL);
    // 參數(shù)1. 隊列名稱
    // 參數(shù)2. 隊列類型稚伍, 串行 還是 并行
    
    // 2. 給隊列添加任務 -(以異步方式添加任務)
    dispatch_async(serialQ, ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"建立網(wǎng)絡鏈接");
    });
// 1. 創(chuàng)建一個并行隊列
dispatch_queue_t concurrentQ = dispatch_queue_create("eg.gcd.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); 

// 創(chuàng)建并行隊列的 全局隊列
    dispatch_queue_t globleQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    // 參數(shù)1. 隊列的優(yōu)先級, 有4種弯予,默認default
    // 參數(shù)2. 預留參數(shù),通常給 0.
    // 注意: 不要使用優(yōu)先級使一個并行隊列个曙,變?yōu)橐粋€串行隊列锈嫩。優(yōu)先級高的執(zhí)行,只是說垦搬,執(zhí)行的概率變高呼寸,并不是最先執(zhí)行。
     
dispatch_async(concurrentQ, ^{  
    // Code here  
});  
// 釋放
dispatch_release(concurrentQ);  

// 并行隊列的特點:雖然也遵守FIFO猴贰,但是提交時对雪,隊列中的任務并不會等待,如果前面的任務沒有執(zhí)行完米绕,不妨礙后面任務的執(zhí)行瑟捣。
// 并行隊列中會開辟多個子線程馋艺。

而系統(tǒng)默認就有一個串行隊列main_queue和并行隊列global_queue:

dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
dispatch_queue_t mainQ = dispatch_get_main_queue();  

通常,我們可以在global_queue中做一些long-running的任務迈套,完成后在main_queue中更新UI捐祠,避免UI阻塞,無法響應用戶操作.

提交到隊列中的不同方式:

  • (1) dispatch_once 這個函數(shù)桑李,它可以保證整個應用程序生命周期中某段代碼只被執(zhí)行一次.
static dispatch_once_t onceToken;  
dispatch_once(&onceToken, ^{  
    // code to be executed once  
});
  • (2) dispatch_after 延時執(zhí)行
// 延遲3s 后踱蛀,改變背景顏色
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.view.backgroundColor = [UIColor redColor];
    });
  • (3) dispatch_apply 執(zhí)行某個代碼片段若干次。
dispatch_apply(10, globalQ, ^(size_t index) {  
    // 參數(shù)1. 重復提交的次數(shù)
    // 參數(shù)2. 提交的隊列
    // 參數(shù)3. 當前提交的次數(shù)  
}); 
  • (4) Dispatch Group 機制監(jiān)聽一組任務是否完成
// 1.創(chuàng)建隊列
    dispatch_queue_t concurrentQ = dispatch_queue_create("q1", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 創(chuàng)建一個組隊列
    // 組隊列用途-- 把一系列的任務放到同一 組中贵白,再提交到隊列星岗。
    dispatch_group_t group = dispatch_group_create();
    
    // 3.添加任務 到組中
    dispatch_group_async(group, concurrentQ, ^{
        NSLog(@"上傳照片");
    });
    dispatch_group_async(group, concurrentQ, ^{
        NSLog(@"獲取授權");
    });
    dispatch_group_async(group, concurrentQ, ^{
        NSLog(@"保存信息");
    });
    
    // 需求:上面的三個操作可以并發(fā)執(zhí)行,最后提交信息戒洼,必須最后執(zhí)行俏橘。
    // 組通知提交方式, 通知提交方式的任務圈浇,等到組中寥掐,若有任務執(zhí)行完畢后,才能執(zhí)行磷蜀。
    dispatch_group_notify(group, concurrentQ, ^{
        NSLog(@"最后提交信息");
    });
  • (5) dispatch_ barrier_ async 障礙提交
// 障礙提交方式一般用在召耘,并行隊列中,當障礙提交方式的任務執(zhí)行時褐隆,后面的任務等待污它。
// 障礙提交方式:只是當前執(zhí)行到此任務時,后面的任務等待庶弃。被障礙分割的上部分 和 下部分 執(zhí)行順序 不確定衫贬。 可能是上部分先執(zhí)行,也可能是下部分先執(zhí)行歇攻。
dispatch_async(concurrentQ, blk0);  
dispatch_async(concurrentQ, blk1);  
 // 添加障礙固惯,執(zhí)行寫入操作,寫入沒有執(zhí)行完之前缴守,不允許讀取數(shù)據(jù)葬毫。
dispatch_barrier_async(concurrentQ, blk_barrier);  
dispatch_async(concurrentQ, blk2);  
  • (6) dispatch_ async_ f 提交函數(shù)
// 聲明一個函數(shù)
void string(void *s)
{
    printf("%s\n",s);
}

- (IBAction)asyncf:(id)sender {
    
    dispatch_async_f(dispatch_get_main_queue(), "aaaa", string);
    // 參數(shù)1. 隊列
    // 參數(shù)2. 傳遞給函數(shù)的參數(shù)
    // 參數(shù)3. 函數(shù)名
    
}
  • (7) dispatch_sync 同步提交
// 同步提交方式 --提交的block,如果沒有執(zhí)行完成屡穗,那么后面的所有代碼都不會執(zhí)行贴捡。
// 也就是說,提交操作村砂,在哪個線程中烂斋,就會阻塞哪個線程

// 注意:不管同步提交方式是提交到哪個線程,一定會阻塞當前線程,執(zhí)行也一定是在當前線程中源祈。
dispatch_queue_t conQ = dispatch_queue_create("q1", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 同步提交 --
    dispatch_sync(conQ, ^{
        
        for (int i = 0; i < 65000; i++) {
            NSLog(@"%@",[NSThread currentThread]);
            NSLog(@"%d",i);
        }
    });

線程互斥:

  • 多個線程同時訪問同一個資源煎源,產(chǎn)生的資源爭奪問題.
static int ticket = 10;
- (IBAction)threadConflict:(id)sender {
    
    dispatch_queue_t concurrentQ = dispatch_queue_create("q1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(10, concurrentQ, ^(size_t i) {
        [self sellTicket];
    });
}

-(void)sellTicket
{
    // 添加同步鎖,如果有一個線程正在訪問香缺,那么其他線程等待
    NSLock *lock = [[NSLock alloc] init];
    
    // 加鎖
    [lock lock];
    NSLog(@"剩余票數(shù)%d",--ticket);
    
    [lock unlock]; // 解鎖
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末手销,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子图张,更是在濱河造成了極大的恐慌锋拖,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祸轮,死亡現(xiàn)場離奇詭異兽埃,居然都是意外死亡,警方通過查閱死者的電腦和手機适袜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門柄错,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苦酱,你說我怎么就攤上這事售貌。” “怎么了疫萤?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵颂跨,是天一觀的道長。 經(jīng)常有香客問我扯饶,道長恒削,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任尾序,我火速辦了婚禮钓丰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹲诀。我一直安慰自己斑粱,他們只是感情好弃揽,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布脯爪。 她就那樣靜靜地躺著,像睡著了一般矿微。 火紅的嫁衣襯著肌膚如雪痕慢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天涌矢,我揣著相機與錄音掖举,去河邊找鬼。 笑死娜庇,一個胖子當著我的面吹牛塔次,可吹牛的內(nèi)容都是我干的方篮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼励负,長吁一口氣:“原來是場噩夢啊……” “哼藕溅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起继榆,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤巾表,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后略吨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體集币,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年翠忠,在試婚紗的時候發(fā)現(xiàn)自己被綠了鞠苟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秽之,死狀恐怖偶妖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情政溃,我是刑警寧澤趾访,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站董虱,受9級特大地震影響扼鞋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜愤诱,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一云头、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淫半,春花似錦溃槐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至对人,卻和暖如春谣殊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背牺弄。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工姻几, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓蛇捌,卻偏偏與公主長得像抚恒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子络拌,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354

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

  • 概覽 大家都知道柑爸,在開發(fā)過程中應該盡可能減少用戶等待時間,讓程序盡可能快的完成運算盒音”眵ⅲ可是無論是哪種語言開發(fā)的程序最...
    周末年安閱讀 1,883評論 1 50
  • NSThread 第一種:通過NSThread的對象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 797評論 0 3
  • 多線程概念 線程線程指的是:1個CPU執(zhí)行的CPU命令列為一條無分叉路徑 多線程這種無分叉路徑不止一條,存在多條即...
    我系哆啦閱讀 580評論 0 5
  • 一、NSThread 1祥诽、創(chuàng)建和啟動線程 2譬圣、其他創(chuàng)建線程方式 上述2種創(chuàng)建線程方式的優(yōu)缺點優(yōu)點:簡單快捷缺點:無...
    小輝輝___閱讀 607評論 0 18
  • 點斑已經(jīng)整兩周了。 現(xiàn)在臉上還有十來個痂沒掉雄坪。掉痂后的皮膚呈有些凹陷的黑紅色厘熟,也不好看,但是比起曾經(jīng)滿臉的黑圈维哈,我...
    一念見花開閱讀 141評論 0 0