你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> 了解iOS多線程

了解iOS多線程

編輯:IOS開發綜合

線程是順序執行的最小單位。它被包括在進程之中,是進程中的實踐運作單位。

多線程是指順序可以同一時間運轉多個線程,以愈加合理天時用零碎資源。

IOS中跟UI顯示相關的操作都在main線程中。為了不阻塞main線程(卡住UI),通常把耗時任務放在其他線程。

IOS多線程有3種運用方式:NSThread、GCD(Grand Central Dispatch)、NSOperation

由於NSThread要自己管理線程的生命周期和同步、加鎖問題,這會招致一定的功能開支,所以開發中通常運用GCD和NSOperation

NSThread

靜態辦法創立

NSThread *thread = [[NSThread alloc] initWithtarget:self selector:@selector(demo:) object:@"alloc"];
// 手動啟動線程
[thread start];

靜態辦法創立

[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];  

隱式創立

[self performSelectorInBackground:@selector(run) withObject:nil];  

獲取以後線程

NSThread *current = [NSThread currentThread];  

在主線程上執行操作

[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES]; 
多線程的平安隱患

1塊資源能夠會被多個線程共享,也就是多個線程能夠會訪問同一塊資源

線程同步互斥、加鎖

為了避免因多線程爭奪資源形成的數據平安問題,需求運用互斥鎖來完成多線程的同步(不同線程的義務按一定的順序執行)

- (void)began{
//    座位一共15個
    self.seat = 15;

//    開啟一個線程 
    NSThread *thread1 = [[NSThread alloc]initWithtarget:self selector:@selector(reserveSeat) object:nil];
    thread1.name = @"thread1";
    [thread1 start];
//    開啟一個線程  
    NSThread *thread2 = [[NSThread alloc]initWithtarget:self selector:@selector(reserveSeat) object:nil];
    thread2.name = @"thread2";
    [thread2 start];
}

-(void)reserveSeat{
//    我們必需座位預定完   也就是不斷循環 直到seat屬性沒有值
    while (true) {
        // 留意,鎖一定要是一切線程共享的對象
        // 假如代碼中只要一個中央需求加鎖,大多都運用 self
        @synchronized(self) {
//            判別假如座位大於0  客戶就可以預訂
            if(self.seat > 0)
            {
                NSLog(@"預定%d號座位  ------%@",self.seat,[NSThread currentThread]);
                self.seat --;
            }else{
                NSLog(@"沒有座位了 ------%@",[NSThread currentThread]);
                break;
            }
        }
    }
}
GCD

純C言語線程庫,提供了十分多弱小的函數
GCD是蘋果公司為多核的並行運算提出的處理方案
GCD會自動應用更多的CPU內核(比方雙核、四核)
GCD會自動管理線程的生命周期(創立線程、調度義務、銷毀線程)
順序員只需求通知GCD想要執行什麼義務,不需求編寫任何線程管理代碼

GCD是純C言語的,因而我們在編寫GCD相關代碼的時分,面對的函數,而不是辦法。
GCD中的函數大少數都以dispatch掃尾。

運用GCD之前需求了解以下幾個概念:

串行隊列:隊列中的義務是一個一個按寄存順序取出來的,前一個義務沒執行完,後一個義務不能取出。

並行隊列:隊列中的義務可以同時取出,前一個義務剛開端執行,後一個義務就能取出了

留意:隊列只是管理義務的調度,不擔任執行

同步執行:會阻塞線程,直到block中的義務執行終了。並且只在main線程中執行

異步執行:以後線程會直接往下執行,它不會阻塞以後線程。只需有閒暇的線程(只需零碎允許,就能創立出新的閒暇線程),就拿來執行

線程池:管理零碎的線程。GCD有一個底層線程池,這個池中寄存的是一個個的線程。之所以稱為“池”,很容易了解出這個“池”中的線程是可以重用的,當一段時間後這個線程沒有被調用的話,這個線程就會被銷毀。留意:開多少條線程是由底層線程池決議的(線程建議控制再3~5條),池是零碎自動來維護,不需求我們順序員來維護

GCD的運用

自定義串行隊列

dispatch_queue_t queue0 = dispatch_queue_create("com.deeepthinking", DISPATCH_QUEUE_SERIAL);
// 同步執行
dispatch_sync(queue0, ^{

});
// 異步執行
dispatch_async(queue0, ^{

});

“com.deeepthinking”是你自定義隊列的標識,用來debug用,可以自己設置。

自定義並行隊列

dispatch_queue_t queue0 = dispatch_queue_create("com.deeepthinking", DISPATCH_QUEUE_CONCURRENT);
// 同步執行
dispatch_sync(queue0, ^{

});
// 異步執行
dispatch_async(queue0, ^{

});

串行隊列+同步執行,義務一個一個執行;
串行對列+異步執行,義務一個一個執行;
並行隊列+同步執行,義務一個一個執行;
並行隊列+異步執行,義務同時停止;

所以要完成真正的並發,要運用並行隊列+異步執行的方式。

將義務放到客隊列中:

dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(main_queue, ^{
});

客隊列是個特殊的串行隊列,通常用來刷新UI

不論是同步執行還是異步執行,客隊列是一定在main線程中執行的。但是同步執行會阻塞main線程,有能夠還會形成死鎖,所以將義務放到主線程執行時,要用異步執行。比方獲取圖片並更新到UI:

    dispatch_queue_t queue1 = dispatch_queue_create("dpt", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue1, ^{
        UIImage *image = [self downloadImage];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });

運用全局隊列:
這是零碎提供的一個並行隊列,方便開發者調用。通常我們做並發義務都是參加到這個隊列。

dispatch_queue_t queue1 = dispatch_get_global_queue(0, 0);
dispatch_async(queue1, ^{
    UIImage *image = [self downloadImage];
    dispatch_async(dispatch_get_main_queue(), ^{
        self.imageView.image = image;
    });
});

除此之外,GCD還有其他一些功用

一次性執行,用來做單例的實例初始化正好:

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // code to be executed once
    });

延遲執行

// 延遲2秒執行:
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        // code to be executed on the main queue after delay
    });

兼並匯總執行,就是義務執行完,會等其他義務執行完,然後執行總義務:

// 兼並匯總後果
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
        // 並行執行的線程一
    });
    dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
        // 並行執行的線程二
    });
    dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
        // 匯總後果
    });
NSOperation

NSOperation 是蘋果公司對 GCD 的封裝,完片面向對象,所以運用起來更好了解。 大家可以看到 NSOperation 和 NSOperationQueue 辨別對應 GCD 的義務和隊列 。操作步驟也很好了解:

將要執行的義務封裝到一個 NSOperation 對象中。
將此義務添加到一個 NSOperationQueue 對象中。
然後零碎就會自動在執行義務。

NSOperation 只是一個籠統類,所以不能封裝義務。但它有 2 個子類用於封裝義務。辨別是:NSInvocationOperation 和 NSBlockOperation 。創立一個 Operation 後,需求調用 start 辦法來啟動義務,它會默許同步執行。

NSInvocationOperation *invoOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationRun) object:nil];
[invoOperation start];

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"#current thread:%@", [NSThread currentThread]);
}];
[blockOperation start];

NSBlockOperation 還有一個辦法addExecutionBlock: ,經過這個辦法可以給 Operation 添加多個執行 Block。這樣 Operation 中的義務會並發執行:

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
}];

for (NSInteger i = 0; i < 5; i++) {
    [operation addExecutionBlock:^{
        NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
    }];
}

[operation start];

除了下面的兩種 Operation 以外,我們還可以自定義 Operation。自定義 Operation 需求承繼 NSOperation 類,並完成其 main() 辦法,由於在調用 start() 辦法的時分,外部會調用 main() 辦法完成相關邏輯。所以假如以上的兩個類無法滿足你的願望的時分,你就需求自定義了。你想要完成什麼功用都可以寫在外面。除此之外,你還需求完成 cancel() 在內的各種辦法。

NSOperation允許我們調用-(void)cancel取消一個操作的執行。當然,這個操作並不是我們所想象的取消。這個取消的步驟是這樣的,假如這個操作在隊列中沒有執行,那麼這個時分取消並將形態finished設置為YES,那麼這個時分的取消就是直接取消了。假如這個操作曾經在執行了,那麼我們只能等其操作完成。當我們調用cancel辦法的時分,他只是將isCancelled設置為YES。所以,在我們的操作中,我們應該在每個操作開端前,或許在每個有意義的實踐操作完成後,先反省下這個屬性是不是曾經設置為YES。假如是YES,則前面操作都可以不必在執行了。

[blockOperation cancel];

操作完成時,將會調用上面這個辦法,這樣也十分方便的讓我們對view停止更新或許添加自己的業務邏輯代碼:

[blockOperation setCompletionBlock:^{
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    [queue addOperationWithBlock:^{
        // 更新UI
    }];
}];

NSOperation 有一個十分適用的功用,那就是添加依賴:

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"下載圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"打水印   - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"上傳圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

[operation2 addDependency:operation1];      //義務二依賴義務一
[operation3 addDependency:operation2];      //義務三依賴義務二

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

NSOperation 對象的 start() 辦法來啟動義務,這樣做他們默許是同步執行的。就算是 addExecutionBlock 辦法,還是會占用以後線程。這是就要用到隊列NSOperationQueue 了。

運用NSOperationQueue默許是並行隊列異步執行的:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
}];

for (NSInteger i = 0; i < 5; i++) {
    [operation addExecutionBlock:^{
        NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
    }];
}

[queue addOperation:operation];

在運用NSOperationQueue進程中,不必管串行、並行、同步、異步這些名詞。NSOperationQueue 有一個參數 maxConcurrentOperationCount 最大並發數,用來設置最多可以讓多少個義務同時執行。當你把它設置為 1 的時分,就是串行隊列。

參考文章

IOS中的多線程技術
iOS多線程篇-NSThread-synchronized(互斥鎖)
iOS開發多線程篇—GCD引見
iOS多線程同步異步、串行並發的團體分析(GCD)
關於iOS多線程,你看我就夠了

【了解iOS多線程】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved