面試錦囊之多線程

??????

多線程相關的話題是面試過程中必不可少的話題。有些面試官可能是要你自己談談對多線程的認識,而有些則是出道題給你,讓你直接手寫或者讓你口述實現(xiàn)。

談談對多線程的認識

在OC中實現(xiàn)多線程的方法有3種(NSThreadGCD、NSOperation),
NSThread是蘋果官方提供的,可以直接操作線程對象。不過需要程序員自己管理線程的生命周期(主要是創(chuàng)建),所以偶爾用用。比如 [NSThread currentThread],它可以獲取當前線程信息,用于調試十分方便。而GCDNSOperation都是系統(tǒng)自動管理線程周期。

GCD是基于C底層的API,是一種更輕量級的,會自動合理地利用更多的CPU內核(比如雙核、四核)。以FIFO(先進先出,后進后出)的順序執(zhí)行任務。GCD中的核心概念:任務 隊列。任務:即操作,你想要干什么,說白了就是一段代碼,在GCD中就是一個 Block,所以添加任務十分方便。任務有兩種執(zhí)行方式: 同步執(zhí)行異步執(zhí)行,他們之間的區(qū)別是是否會創(chuàng)建新的線程。有串行、并發(fā)、主隊列、全局隊列、組隊列。其中隊列任務組合的方式總共有7種,其中使用頻率最高的是:并發(fā)隊列+異步執(zhí)行(多個任務同時執(zhí)行并發(fā)執(zhí)行,會開啟多條線程)。關于GCD的一些其他具體組合使用方式,可查閱筆者之前的文章。點我查看

NSOperation是基于GCD更高一層的封裝,相對于GCD更加強大??梢越ooperation之間添加依賴關系、取消一個正在執(zhí)行的operation、暫停和恢復operationQueue等。關于NSOperation的其他一些知識點,可查閱筆者之前的文章。點我查看

多線程的面試題目

  • (1)A,B,C三個線程,要求執(zhí)行完A,B后才能執(zhí)行C,怎么做?

實現(xiàn)思路一: 添加依賴關系,A、B都依賴于C

     //1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    //2.創(chuàng)建操作
    NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"A----%@",[NSThread currentThread]); 
    }];
    
    NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"B----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"C----%@",[NSThread currentThread]);        
    }];
    
    //3.添加依賴
    [operationC addDependency:operationA]; // 讓C 依賴于 A,則先執(zhí)行A,再執(zhí)行C
    [operationC addDependency:operationB]; // 讓C 依賴于 B,則先執(zhí)行B,再執(zhí)行C
    //4.添加操作到隊列中
    [queue addOperation:operationA];
    [queue addOperation:operationB];
    [queue addOperation:operationC];
思路一打印結果.png

實現(xiàn)思路二:dispatch_group_notify

    dispatch_queue_t queue = dispatch_queue_create("label", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
         NSLog(@"A----%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"B----%@",[NSThread currentThread]);
    });
    //等前面的任務執(zhí)行完畢后 會自動執(zhí)行這個任務
    dispatch_group_notify(group, queue, ^{ 
         NSLog(@"C----%@",[NSThread currentThread]);
    });
思路二打印結果.png

實現(xiàn)思路三:dispatch_barrier_(a)sync

    dispatch_queue_t queue = dispatch_queue_create("label", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"A----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"B----%@",[NSThread currentThread]);
    });
 // dispatch_barrier_async: 在它前面的任務執(zhí)行結束后它才執(zhí)行,在它后面的任務等它執(zhí)行完成后才會執(zhí)行
    dispatch_barrier_async(queue, ^{
        NSLog(@"C----%@",[NSThread currentThread]);
    });
思路一打印結果.png
  • (2)結合使用AFNetworking多次請求,實現(xiàn)線程同步以及依賴。

在使用AFNetworking之前,我們首先看一段代碼
需求:吃飯 睡覺 打豆豆 -> 任務完成

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{        
        NSLog(@"吃飯");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"睡覺");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"打豆豆");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任務完成");
    });
圖片.png

那么使用AFNetworking之后呢,會觸發(fā)怎么的結果呢?我們一起拭目以待。

- (void)requestFormData{
    
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        [self requestWithType:@"吃飯"];
    });

    dispatch_group_async(group, queue, ^{
        [self requestWithType:@"睡覺"];
    });
    
    dispatch_group_async(group, queue, ^{
        [self requestWithType:@"打豆豆"];
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任務完成");
    });
}
- (void)requestWithType:(NSString *)type{

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager POST:@"http://www.mocky.io/v2/5b6685533200006a00ee11b1" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
              
        NSLog(@"%@",type);
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {}];    
}
圖片.png

綜上打印,你心里可能會產生一個疑惑,只是換成了AFNetworking,打印結果卻發(fā)生了變化,用過AFNetworking的伙伴們應該都知道在進行網(wǎng)絡請求的時候內部是又開了線程的,那么這時候我們應該怎么實現(xiàn)吃飯睡覺打豆豆這個需求呢,請接著繼續(xù)往下看。

方式一:使用信號量進行破解

- (void)requestWithType:(NSString *)type{
    
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager POST:@"http://www.mocky.io/v2/5b6685533200006a00ee11b1" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {       

         long flag  =   dispatch_semaphore_signal(semaphore); 
        NSLog(@"%@ --%ld",type,flag);

    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        dispatch_semaphore_signal(semaphore);
    }];
    
    long flag  =  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  
    NSLog(@"%@ --%ld",type,flag);
}

圖片.png

方式二:dispatch_group_enterdispatch_group_leave配合使用

- (void)requestFormData{
    
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        [self requestWithType:@"吃飯" group:group];        
    });

    dispatch_group_async(group, queue, ^{
        [self requestWithType:@"睡覺" group:group];
    });
    
    dispatch_group_async(group, queue, ^{
        [self requestWithType:@"打豆豆" group:group];
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       
        NSLog(@"任務完成");
    });
}
- (void)requestWithType:(NSString *)type group:(dispatch_group_t)group{
    
//通知group,下面的任務馬上要放到group中執(zhí)行了
     dispatch_group_enter(group);
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager POST:@"http://www.mocky.io/v2/5b6685533200006a00ee11b1" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
       
//通知group,任務完成了,該任務要從group中移除了
        dispatch_group_leave(group);
        NSLog(@"%@",type);
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
       dispatch_group_leave(group);
    }];
}
圖片.png

如果上面對你來說太so seay了,于是在此基礎添加一個要求,吃完飯后,打會豆豆,之后便允許睡覺,才算完成任務了。革命尚未成功,同志仍需努力????
使用AFNetworking請求,實現(xiàn)線程同步以及依賴

- (void)requestFormData{
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        [self requestWithType:@"吃飯"];
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        [self requestWithType:@"睡覺"];
    }];
    
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        [self requestWithType:@"打豆豆"];
    }];
    
    NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"任務完成");
        }];
    }];

    [operation2 addDependency:operation1];      
    [operation4 addDependency:operation1];      
    [operation4 addDependency:operation2];      
    [operation4 addDependency:operation3];
 
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation1,operation2, operation3, operation4] waitUntilFinished:NO];

}
- (void)requestWithType:(NSString *)type {
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager POST:@"http://www.mocky.io/v2/5b6685533200006a00ee11b1" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
       
        NSLog(@"%@",type);
        
    dispatch_semaphore_signal(semaphore);
        
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        dispatch_semaphore_signal(semaphore);

    }];
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

圖片.png

知識點補給站

  • addDependency不能添加相互依賴,例如:A依賴B,B依賴A,這樣會導致死鎖

  • 創(chuàng)建信號量,可以設置信號量的資源數(shù)。0表示沒有資源。如:dispatch_semaphore_t semaphore = dispatch_semaphore_create(0)

  • 等待信號,會讓信號量值減一,當信號量值為0時會等待(直到超時),否則正常執(zhí)行;如:dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

  • 發(fā)送一個信號,會讓信號總量加1。如:dispatch_semaphore_signal(semaphore)


  • dispatch_group_enter必須在dispatch_group_leave之前出現(xiàn)
  • dispatch_group_enterdispatch_group_leave必須成對出現(xiàn)
  • 如果dispatch_group_enterdispatch_group_leave多一次,則wait函數(shù)等待的線程不會被喚醒和注冊notify的回調block不會執(zhí)行
  • 如果dispatch_group_leavedispatch_group_enter多一次,則會引起崩潰。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 本文用來介紹 iOS 多線程中 GCD 的相關知識以及使用方法。這大概是史上最詳細、清晰的關于 GCD 的詳細講...
    花花世界的孤獨行者閱讀 576評論 0 1
  • 轉載自:http://www.itdecent.cn/p/665261814e24 談到iOS多線程,一般都會談到...
    CodingMann丶許半仙閱讀 505評論 0 0
  • 一、多線程簡介: 所謂多線程是指一個 進程 -- process(可以理解為系統(tǒng)中正在運行的一個應用程序)中可以開...
    尋形覓影閱讀 1,183評論 0 6
  • ? 前言:這可能是史上最全面的一篇iOS 多線程博客了(王婆賣瓜一番??),從多線程的基本概念,進程的概念,引出i...
    阿餅six閱讀 883評論 2 11
  • 正值春運,人的神經會趨于同步的感覺,出門即掂記會較平日緊張,車流趨密、人流趨織。于是乎,人沒有在機場坐定,就會往前...
    c29b18170e8a閱讀 230評論 0 0

友情鏈接更多精彩內容