線程是順序執行的最小單位。它被包括在進程之中,是進程中的實踐運作單位。
多線程是指順序可以同一時間運轉多個線程,以愈加合理天時用零碎資源。
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多線程】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!