概述
- 把程序中耗時的任務放到後臺去處理,如圖片、視頻的下載等;
- 充分發揮多核處理器的優勢,併發執行讓系統運行的更快、更流暢、用戶體驗更佳。
- 大量的線程操作會降低代碼的可讀性;
- 大量的線程需要更多的內存空間;
- 當多個線程對同一資源出現爭奪的時候會出現線程安全問題。
- pthread:是一套基於C語言的通用多線程API,線程的生命週期需要我們手動管理,使用難度較大,所以我們幾乎不會使用。
- NSThread:是一套面向對象的API,線程的生命週期需要我們手動管理,簡單易用,可直接操作線程對象。
- GCD:是一套底層基於C語言的多線程API,自動管理生命週期,充分利用了多核處理器的優點。
- NSOperation:是一套底層基於GCD面向對象的多線程API,自動管理生命週期,並且比GCD多了一些更簡單實用的功能。
在這裏我們暫且不討論pthread的使用,主要看看後面三個方案都是怎麼應用的。
NSThread
1. 先創建再啓動
// 創建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"Axe"];
// 啓動
[thread start];
2. 直接創建並啓動線程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"Axe"];
3. 創建並啓動
[self performSelectorInBackground:@selector(run:) withObject:@"Axe"];
[NSThread sleepForTimeInterval:2.0];
從三種創建線程的方法可以看到:方法2和3都可以更方便的創建一個線程,並且自啓動。而方法一可以很方便的拿到線程對象。
4.應用示例
NSURL *url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/203fb80e7bec54e753da379aba389b504fc26a7b.jpg"];
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImageWithURL:) object:url];
[thread start];
- (void)downloadImageWithURL:(NSURL *)url {
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:&error];
if (error) {
NSLog(@"error = %@",error);
} else {
UIImage *image = [UIImage imageWithData:data];
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
}
}
GCD
- 同步線程:在當前線程可立即執行任務,不具備開啓線程的能力,會阻塞當前的線程,必須等待同步線程中的任務執行完並返回後,纔會執行下一個任務。
- 異步線程:在當前線程結束後執行任務,具備開啓新的線程的能力。
- 並行隊列:允許多個任務同時執行。
- 串行隊列:只有等上一個任務執行完畢後,下一個任務纔會被執行。
創建串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
創建並行隊列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
創建同步+串行隊列
//在當前線程,立即執行任務
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"%@",[NSThread currentThread]);
}
});
創建同步+並行隊列
//在當前線程,立即執行任務
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"%d --- %@",i, [NSThread currentThread]);
}
});
創建異步+串行隊列
//另開啓線程,多個任務順序執行
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
dispatch_async(serialQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"%d --- %@",i, [NSThread currentThread]);
}
});
dispatch_async(serialQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"%d --- %@",i, [NSThread currentThread]);
}
});
});
創建異步+並行隊列
//另開啓線程,多個任務一起執行,不分先後
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
dispatch_async(serialQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"%d --- %@",i, [NSThread currentThread]);
}
});
dispatch_async(serialQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"%d --- %@",i, [NSThread currentThread]);
}
});
});
應用示例
NSURL *url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/203fb80e7bec54e753da379aba389b504fc26a7b.jpg"];
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.download.image", DISPATCH_QUEUE_CONCURRENT);
__weak typeof(self) weakSelf = self;
dispatch_async(concurrentQueue, ^{
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:&error];
if (error) {
NSLog(@"error = %@",error);
} else {
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.imageView.image = image;
});
}
});
GCD其他函數的應用
dispatch_barrier 柵欄
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"1-->%d --- %@",i, [NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"2-->%d --- %@",i, [NSThread currentThread]);
}
});
dispatch_barrier_async(concurrentQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"3-->%d --- %@",i, [NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"4-->%d --- %@",i, [NSThread currentThread]);
}
});
通過log輸出可以看到,1和2隊列的任務會先無序執行,在其兩個隊列中的任務執行完畢後,纔會執行柵欄隊列中的任務,最後才執行隊列4。dispatch_after 延遲
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
dispatch_once 執行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 執行一次
});
dispatch_apply 快速迭代
NSArray *array = @[@"a",@"b",@"c",@"d",@"e"];
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(array.count, globalQueue, ^(size_t idx) {
NSLog(@"array[%zu] = %@",idx, array[idx]);
});
dispatch_group 隊列組
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, globalQueue, ^{
NSLog(@"任務1");
});
dispatch_group_async(group, globalQueue, ^{
NSLog(@"任務3");
});
dispatch_group_async(group, globalQueue, ^{
NSLog(@"任務2");
});
dispatch_group_notify(group, globalQueue, ^{
NSLog(@"所有任務都已完成");
});
dispatch_group_async(group, globalQueue, ^{
NSLog(@"任務4");
});
GCD定時器
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, globalQueue);
// 3s後定時器啓動
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
// 每1秒執行一次回調
dispatch_source_set_timer(timer, start, 2.0 * NSEC_PER_SEC, 0);
// 計時
__block int count = 0;
dispatch_source_set_event_handler(timer, ^{
NSLog(@"%d", count);
if (count > 10) {
dispatch_cancel(timer);
}
count++;
});
dispatch_resume(timer);
NSOperation
NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImageWithURL:) object:url];
[invocationOperation start];
NSBlockOperation
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 在主線程中執行
NSLog(@"操作1 --- %@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
// 在子線程中執行
NSLog(@"操作2 --- %@", [NSThread currentThread]);
}];
// 添加額外的任務數大於1纔會異步執行
[blockOperation addExecutionBlock:^{
// 在子線程中執行
NSLog(@"操作3 --- %@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
// 在子線程中執行
NSLog(@"操作4 --- %@", [NSThread currentThread]);
}];
[blockOperation start];
Operation的其它用法
執行操作
取消操作
操作完成後的操作
自定義Operation
//
// MyOperation.h
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef void(^MyDownloadFinishedBlock)(UIImage *image);
@interface MyOperation : NSOperation
@property (copy, nonatomic, readonly) NSString *imageURL;
- (instancetype)initWithURLString:(NSString *)urlString downloadFinishedBlock:(MyDownloadFinishedBlock)downloadFinishedBlock;
@end
//
// MyOperation.m
//
#import "MyOperation.h"
@interface MyOperation ()
@property (copy, nonatomic) MyDownloadFinishedBlock downloadFinishedBlock;
@end
@implementation MyOperation
- (instancetype)initWithURLString:(NSString *)urlString downloadFinishedBlock:(MyDownloadFinishedBlock)downloadFinishedBlock {
self = [super init];
if (self) {
_imageURL = urlString;
_downloadFinishedBlock = downloadFinishedBlock;
}
return self;
}
- (void)main {
// 如果時在異步線程中執行操作,即main方法在異步線程中調用,那麼將無法訪問主線程的自動釋放池,因此創建一個屬於當前線程的自動釋放池
@autoreleasepool {
// 在main方法中定期的調用isCancelled方法檢測操作是否已經被取消
// 在執行任何實際的工作之前,也就是main方法的開頭,就要檢測操作是否已經被取消
// 在執行一段耗時的操作後也需要檢測操作是否已經被取消
if (self.isCancelled) {
return;
}
NSURL *url = [NSURL URLWithString:_imageURL];
// 此處就是通過網絡獲取圖片data,是比較耗時的操作,所以在後面就需要檢測操作是否已經被取消
NSData *imageData = [NSData dataWithContentsOfURL:url];
if (self.isCancelled) {
url = nil;
_imageURL = nil;
return;
}
UIImage *image = [UIImage imageWithData:imageData];
if (self.isCancelled) {
image = nil;
return;
}
if (_downloadFinishedBlock) {
_downloadFinishedBlock(image);
}
}
}
@end
MyOperation *operation = [[MyOperation alloc] initWithURLString:@"http://f.hiphotos.baidu.com/image/pic/item/203fb80e7bec54e753da379aba389b504fc26a7b.jpg" downloadFinishedBlock:^(UIImage *image) {
NSLog(@"%@",[NSThread currentThread]);
[_imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
}];
[operation start];
NSOperationQueue
添加NSOperation到NSOperationQueue中
添加一個operation
[operationQueue addOperation:invocationOperation];
添加一組operation
[operationQueue addOperations:@[invocationOperation, blockOperation] waitUntilFinished:YES];
添加一個block形式的operation
[operationQueue addOperationWithBlock:^{
NSLog(@"任務");
}];
添加依賴
概述
限制依賴
應用示例:
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1.上傳頭像");
}];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2.從服務器請求上傳頭像的url");
}];
NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3.通過url下載頭像");
}];
[operationQueue addOperation:blockOperation1];
[operationQueue addOperation:blockOperation2];
[operationQueue addOperation:blockOperation3];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1.上傳頭像");
}];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2.從服務器請求上傳頭像的url");
}];
NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3.通過url下載頭像");
}];
// 3 -> 2 2 -> 1 (3 - 2 - 1)
[blockOperation3 addDependency:blockOperation2];
[blockOperation2 addDependency:blockOperation1];
[operationQueue addOperation:blockOperation1];
[operationQueue addOperation:blockOperation2];
[operationQueue addOperation:blockOperation3];
打印順序如下:1 - 2 - 3