iOS多線程總結

今天沒什麼事做就對iOS的多線程做一次總結,純屬個人看法,初學者可以參考下。對於多線程我想無論是面試還是實際開發大家都不會陌生;

嚴格意義來講iOS多線程算是4種:

  1. PThreads
  2. NSThread
  3. GCD
  4. NSOPeration/NSOperationQueue;

PThreads

這個是基於C語音定義的POSIX Threads(簡稱PThreads)標準線程,但是對於iOS開發者使用率很低,原因是有二:
  1. 調用的都是C函數和語法,對於習慣OC或者Swift開發的我們來說,很蛋疼;
  2. 線程的生命週期需要我們手動管理。
這個一般用在實現操作系統和PHP裏(UnixLinuxMac OS X等)
- (void)Pthreads {
    pthread_t thread;
    
    //創建一個線程並執行 start是一個函數指針
    pthread_create(&thread, NULL, start, NULL);
}

void *start(void *data) {
    //這裏執行線程操作
    NSLog(@"%@",[NSThread currentThread]);
    return NULL;
}
有興趣的同學可以看看。

NSThread

這個是蘋果自己開發,NSThread是輕量級多線程生命週期需要手動管理因爲擴展性不高,有很多侷限性只能偶爾用用 。
上代碼:
- (void)NSThread {
    
    //先創建後執行
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
    [thread start];
    
    //創建並執行
    [NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];
    
    //perform裏唯一一個多線程方法 如果線程方法要刷新UI可以使用
    //- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    [self performSelectorInBackground:@selector(threadRun) withObject:nil];
}

- (void)threadRun {
    
    NSLog(@"threadRun:%@",[NSThread currentThread]);
}

GCD

下面是重點,Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法。GCD能充分利用手機性能,讓運行效率更快,也是蘋果主推的多線程開發方式。
在GCD中,有兩個概念很重要就是任務和隊列,任務分同步執行和異步執行兩種方式,隊列也分串行隊列和並行隊列,下面我們來看它們的創建和使用:

//1.初始化/獲取系統隊列
    //獲取主隊列 ()一般只負責刷新UI,耗時任務都交給其他線程
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    //創建自己的隊列
    //串行隊列
    dispatch_queue_t queue1 = dispatch_queue_create("隊列標識符", nil);
    
    dispatch_queue_t queue2 = dispatch_queue_create("隊列標識符", DISPATCH_QUEUE_SERIAL);
    
    //並行隊列
    dispatch_queue_t queue3 = dispatch_queue_create("隊列標識符", DISPATCH_QUEUE_CONCURRENT);
    
    //獲取全局並行隊列  只要是並行任務一般都加入到這個隊列。這是系統提供的一個併發隊列。
    dispatch_queue_t queue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //2.任務的創建和執行方法(同步和異步)
    //同步任務
    dispatch_sync(queue3, ^{
        
        NSLog(@"GCD-同步%@",[NSThread currentThread]);
        sleep(5);
        NSLog(@"GCD-同步—後%@",[NSThread currentThread]);
    });
    
    //異步任務
    dispatch_async(queue3, ^{
        
        NSLog(@"GCD-異步%@",[NSThread currentThread]);
    });

從上面的代碼可以看出隊列還主隊列(系統隊列)還有自定義隊列(多線程),自定義隊列創建的時候兩個參數第一個是標識符可以爲空;第二個爲空或者DISPATCH_QUEUE_SERIAL爲串行隊列,DISPATCH_QUEUE_CONCURRENT爲並行隊列。還有一個全局並行隊列一般的併發任務都會加入這個隊列,其實無論是並行隊列還是串行隊列都是以先進先出原則,區別在於並行隊列是取一個開一個線程執行,在取下一個,中間取的時間很快;串行隊列是取出一個執行完成以後再取出下一個,一個一個的執行。需要注意的是GCD會根據系統資源限制並行數量。
//3.隊列組 隊列組可以將很多隊列添加到一個組裏,這樣做的好處是,當這個組裏所有的任務都執行完了,隊列組會通過一個方法通知我們。
    //3.1創建隊列組
    dispatch_group_t group = dispatch_group_create();
    //3.2創建隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //3.3多次使用隊列組的方法執行任務,只有異步方法
    //執行3此循環
    dispatch_group_async(group, queue, ^{
        
        for (int i = 0; i < 3; i++) {
            NSLog(@"group-01:%@",[NSThread currentThread]);
        }
        
    });
    
    //主隊列執行8此循環
    dispatch_group_async(group, dispatch_get_main_queue(), ^{
       
        for (int i = 0; i < 8; i++) {
            NSLog(@"group-02:%@",[NSThread currentThread]);
        }
    });
    
    //執行5次循環
    dispatch_group_async(group, queue, ^{
       
        for (int i = 0; i < 5; i++) {
            NSLog(@"group-03:%@",[NSThread currentThread]);
        }
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        
        NSLog(@"完成-04:%@",[NSThread currentThread]);
    });

NSOperation/NSOperationQueue

NSOperation 是蘋果公司對 GCD的封裝,完全面向對象,所以使用起來更好理解。大家可以看到 NSOperation NSOperationQueue分別對應 GCD任務隊列

 NSOperation只是一個抽象類,所以不能封裝任務。但它有 2個子類用於封裝任務。

 分別是:NSInvocationOperation NSBlockOperation

 創建一個 Operation後,需要調用 start方法來啓動任務,它會默認在當前隊列同步執行。

 當然你也可以在中途取消一個任務,只需要調用其 cancel方法即可


//1.創建
    //NSInvocationOperation
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
    //開始執行
    [operation start];
    
    //NSBlockOperation
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
       
        NSLog(@"blockOperation:%@",[NSThread currentThread]);
    }];
    
    /*
    NSBlockOperation 還有一個方法:addExecutionBlock: ,通過這個方法可以給 Operation 添加多個執行 Block。這樣 Operation 中的任務會併發執行,它會在主線程和其它的多個線程 執行這些任務
     */
    for (int i = 0; i < 5; i++) {
        [blockOperation addExecutionBlock:^{
           
            NSLog(@"blockOperation-第%d次:%@",i,[NSThread currentThread]);
        }];
    }
    [blockOperation start];
    /*
     看過上面的內容就知道,我們可以調用一個 NSOperation 對象的 start() 方法來啓動這個任務,但是這樣做他們默認是 同步執行 的。就算是 addExecutionBlock 方法,也會在 當前線程和其他線程 中執行,也就是說還是會佔用當前線程。這是就要用到隊列 NSOperationQueue 了。而且,按類型來說的話一共有兩種類型:主隊列、其他隊列。只要添加到隊列,會自動調用任務的 start() 方法
     */
    //NSOperationQueue 獲取主隊列(串行)
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    
    //創建其他隊列
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"%@",[NSThread currentThread]);
    }];
    //這裏可以添加多個operation
    [queue1 addOperation:operation1];

NSOperation 有一個非常實用的功能,那就是添加依賴。比如有 3個任務:A:從服務器上下載一張圖片,B:給這張圖片加個水印,C:把圖片返回給服務器。這時就可以用到依賴了:

//1.任務一:下載圖片
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"下載圖片:%@",[NSThread currentThread]);
    }];
    //2.任務二:打水印
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"打水印:%@",[NSThread currentThread]);
    }];
    //3.任務三:上傳圖片
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"上傳圖片:%@",[NSThread currentThread]);
    }];
    //4.設置依賴  注意:不能添加相互依賴,會死鎖,比如 A依賴B,B依賴A。
    [operation2 addDependency:operation1];  //任務二依賴任務一
    [operation3 addDependency:operation2];  //任務三依賴任務二
    
    //5.創建隊列並加入
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:[NSArray arrayWithObjects:operation1,operation2,operation3, nil] waitUntilFinished:NO];
以上只是一些基本使用方法,想了解更多可以去網上找或者去看官方文檔;

使用多線程我們就需要考慮數據同步的問題

其他

互斥鎖:給需要同步的代碼塊加一個互斥鎖,就可以保證每次只有一個線程訪問此代碼塊。

@synchronized(self) {
        //需要執行的代碼塊
    }
我們也可以在隊列的結尾加一個串行隊列,來達到數據同步的效果方法有很多。
參考http://www.jianshu.com/p/0b0d9b1f1f19
Demo下載地址:http://download.csdn.net/download/pianzhidenanren/9478706

發佈了42 篇原創文章 · 獲贊 9 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章