當我們的APP在運行的時候就是一個進程,而進程啟動的時候會創建一個線程,這個線程我們叫主線程。主線程一般用于更新UI,所以我們也可以叫做UI線程。一條線程只能做一件事情,當我們在下載資源或者播放音頻的時候需要做其他事情而不想被打斷的時候,就需要多個線程進行處理,這些線程叫子線程。
多線程開發分為以下三種(常見的)
一、NSThreadNSThread是輕量級的多線程開發,由于它是經過蘋果封裝的而且完全面向對象,所以我們用起來很直觀。但是使用NSThread需要我們自己管理線程的生命周期,所以很少用到它。用的最多的就是獲取當前線程信息。下面是它的一些用法
//創建線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(go:) object:nil];
//啟動線程
[thread start];
//創建線程并自啟動
[NSThread detachNewThreadSelector:@selector(come:) toTarget:self withObject:nil];
//獲取當前線程
NSLog(@"%@",[NSThread currentThread]);
//獲取主線程
NSLog(@"%@",[NSThread mainThread]);
//檢查當前線程是否在執行,完成,或者取消
NSLog(@"%d",[NSThread currentThread].executing);
NSLog(@"%d",[NSThread currentThread].finished);
NSLog(@"%d",[NSThread currentThread].cancelled);
注意:如果上面創建的2個線程都實現了各自的方法并打印,會發現運行多次打印出來的順序是不一樣的。因為每個線程的實際執行順序不一定按順序執行。
二、GCD(Grand Central Dispatch)
GCD是用的比較多的一種多線程開發方式,它基于C語言開發,而且使用了block,所以使用起來比較方便,靈活。相比于NSThread,GCD不需要手動管理線程的生命周期。
在GCD中有2個非常重要的概念:任務、隊列
任務:就是里面block執行的任務,分為同步和異步。同步,只有block里面的任務執行完成后,當前的線程才會運行(阻塞當前線程)。異步則是在執行任務的同時,當前線程會繼續運行。
隊列:用于存放任務,分為串行隊列和并行隊列。串行隊列里面的任務,會根據FIFO(先進先出)的順序執行。并行隊列相比于串行,它是開多個線程一起執行(異步情況下)
//創建主隊列(特殊的串行隊列)
dispatch_queue_t queue = dispatch_get_main_queue();
//創建隊列? 第一個參數用于DEBUG的時候標識唯一的隊列,可以為空。第二個參數表示串行或并行隊列
//串行隊列
dispatch_queue_t ACqueue = dispatch_queue_create("JL.GCD", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t BCqueue = dispatch_queue_create("JL.GCD", NULL);
//并行隊列
dispatch_queue_t ABqueue = dispatch_queue_create("JL.GCD", DISPATCH_QUEUE_CONCURRENT);
//全局并行隊列 系統提供的隊列
dispatch_queue_t Bqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創建任務
//同步任務 會阻塞當前線程
dispatch_sync(queue, ^{
NSLog(@"11111%@",[NSThread currentThread]);
});
//異步任務 不會阻塞線程(另外開線程)
dispatch_async(ACqueue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
當有多個隊列時可以將其放到一組,GCD提供了這樣一個組:隊列組。當這個組內的隊列任務執行完了之后會發一個通知給我們。
-(void)group{
//創建隊列組
dispatch_group_t group = dispatch_group_create();
//創建隊列
dispatch_queue_t Myqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//將隊列加入隊列組
dispatch_group_async(group, Myqueue, ^{
for (int i = 0; i<3; i++) {
NSLog(@"group-1 = %@",[NSThread currentThread]);
}
});
//將主隊列加入隊列組
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (int i = 0; i<5; i++) {
NSLog(@"main = %@",[NSThread currentThread]);
}
});
//組內隊列執行完畢發出通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 %@",[NSThread currentThread]);
});
},
三、NSOperation
NSOperation是蘋果公司對GCD的封裝,完全面向對象,使用NSOperation進行多線程開發類似于C#中的線程池,只要將要執行的任務封裝到一個NSOperation對象中(準確的來說是它的兩個子類),然后再將此任務添加到NSOperationQueue中,線程就會依次啟動。
1、NSInvocationOperation
-(void)invocationCreate{
//創建NSInvocationOperation對象
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(OP1:) object:nil];
//當NSInvocationOperation對象調用start方法啟動的時候,改操作會在主線程中調用,一般都不會直接調用,而是添加到NSOperationQueue隊列中。
//? ? [invocationOperation start];
//創建NSOperationQueue隊列
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
//將NSInvocationOperation對象添加到隊列中,此時隊列會開啟一個線程執行該操作
[operationQueue addOperation:invocationOperation];
}
-(void)OP1:(NSInvocationOperation *)sender{
NSLog(@"%@",[NSThread currentThread]);
}
2、NSBlockOperation
//創建blockOperation
-(void)blockCreate{
NSBlockOperation *blockOperation =[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block = %@",[NSThread currentThread]);
}];
//啟動與NSInvocationOperation一樣
//? ? [blockOperation start];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
[operationQueue addOperation:blockOperation];
}
NSBlockOperation除了這個方法還有第二個方法,它可以給Operation添加多個block,這樣Operation中的任務會并發執行,它會在主線程和其他線程執行。
-(void)blockCreate{
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
//設置最大并發線程數
operationQueue.maxConcurrentOperationCount = 3;
for (int i = 0; i<3; i++) {
[operationQueue addOperationWithBlock:^{
NSLog(@" 第%d次 = %@",i,[NSThread currentThread]);
}];
}
}
使用NSBlockOperation,所有的操作都不需要單獨自定義方法,同時解決了只能傳遞一個參數的問題;使用NSOperation可以設置最大并發線程,可以有效的對線程進行控制
NSOperation還有一個很重要的特點:依賴性。利用依賴控制線程的執行順序。
-(void)dependency{
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@" 加載其他圖片: %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"加載最后一張圖片 : %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//blockOperation1依賴于blockOperation2,使得blockOperation1在blockOperation2執行后才執行
[blockOperation1 addDependency:blockOperation2];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
[operationQueue addOperations:@[blockOperation1,blockOperation2] waitUntilFinished:NO];
}
值得注意的是,如果設置了循環依賴的話,會造成死鎖。導致線程不會被執行。如果需要解除依賴關系,可以使用removeDependency來解除
四、線程同步
多線程往往是多個線程并發執行的,同個資源可能被多個線程同時訪問,造成資源搶奪。這個時候我們就需要采取一些措施。
@synchronized代碼塊:@synchronized中的代碼執行時先檢查同步對象是否被另一個線程占用,如果是便會處于等待狀態,直到同步對象被釋放。
@synchronized(self) {
//需要執行的代碼塊
}
本人對多線程的理解還不是很深刻,暫時就寫到這,接下來會繼續更新。