1.線程的狀態(tài):
2.GCD
//1.創(chuàng)建隊(duì)列
/*
參數(shù)1: C語言字符串,是一個(gè)標(biāo)簽,用來區(qū)分隊(duì)列
參數(shù)2: DISPATCH_QUEUE_CONCURRENT -> 并發(fā)隊(duì)列
DISPATCH_QUEUE_SERIAL -> 串行隊(duì)列
*/
//串行隊(duì)列 串行隊(duì)列只會(huì)開辟一個(gè)子線程
dispatch_queue_t queue = dispatch_queue_create("GCD", DISPATCH_QUEUE_SERIAL);
//并發(fā)隊(duì)列 并發(fā)隊(duì)列可以開辟多個(gè)子線程
dispatch_queue_t queue = dispatch_queue_create("GCD", DISPATCH_QUEUE_CONCURRENT);
//主隊(duì)列 添加到主隊(duì)列中的任務(wù)意味著任務(wù)都會(huì)在主線程中執(zhí)行
dispatch_queue_t queue = dispatch_get_main_queue();
/*
參數(shù)1: 隊(duì)列的優(yōu)先級(jí)
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
優(yōu)先級(jí)從高到低的排序
參數(shù)2: 隊(duì)列的標(biāo)簽
*/
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//2.封裝任務(wù)
/*
參數(shù)1: queue 把任務(wù)添加到這個(gè)隊(duì)列中
參數(shù)2: block塊 用于添加任務(wù)
*/
//異步函數(shù)
dispatch_async(queue, ^{
//在這里執(zhí)行想要執(zhí)行的任務(wù),GCD會(huì)自動(dòng)根據(jù)上下文代碼判斷是否需要開辟線程
});
//同步函數(shù)
dispatch_sync(queue, ^{
//同步函數(shù)不開線程,直接再主線程中執(zhí)行任務(wù)
});
注意: 不能再主線程中再次滴啊用同步行數(shù)并且把任務(wù)添加到主隊(duì)列,會(huì)導(dǎo)致線程死鎖
//如果當(dāng)前線程已經(jīng)是主線程,再調(diào)用這調(diào)用這個(gè)函數(shù)來回到主線程的話,會(huì)出現(xiàn)崩潰
dispatch_sync (dispatch_get_main_queue(), ^{
NSLog(@"abc");
});
3.線程安全問題
在進(jìn)行多線程開發(fā)的時(shí)候,經(jīng)常會(huì)發(fā)生不同的線程在同一時(shí)間對(duì)同一個(gè)數(shù)據(jù)進(jìn)行操作,那么這個(gè)時(shí)候我們需要對(duì)數(shù)據(jù)資源進(jìn)行加鎖,以保證數(shù)據(jù)源的安全:
假設(shè)我們有下面一種情況使用多線程對(duì)一個(gè)數(shù)字進(jìn)行不斷的減法
#pragma mark 線程安全
-(void)asyncMethod1 {
dispatch_queue_t q = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(q , ^{
[self subMethod:@"線程1"];
});
dispatch_async(q, ^{
[self subMethod:@"線程2"];
});
dispatch_async(q, ^{
[self subMethod:@"線程3"];
});
//真實(shí)的情況中可能會(huì)是在不同的地方都用了異步并行的方式調(diào)用了同一個(gè)方法
}
/#pragma mark 執(zhí)行減法操作
-(void)subMethod:(NSString *)stu{
while (1) {
if (self.numbers > 0) {
for (int i = 0; i< 100000; i++) {}
self.numbers -= 1;
NSLog(@"%@使用了減法后剩余:%d",stu,self.numbers);
}else{
NSLog(@"%@發(fā)現(xiàn)減數(shù)為0",stu);
break;
}
}
}
/*
當(dāng)調(diào)用 asyncMethod1 方法時(shí)
控制臺(tái)最后打印了如下數(shù)據(jù):
->線程1使用了減法后剩余:0
->線程1發(fā)現(xiàn)減數(shù)為0
->線程2使用了減法后剩余:-1
->線程2發(fā)現(xiàn)減數(shù)為0
->線程3使用了減法后剩余:-2
->線程3發(fā)現(xiàn)減數(shù)為0
*/
我們明明已經(jīng)對(duì)numbers進(jìn)行判斷,只有大于0的時(shí)候才進(jìn)行減1操作,為什么還會(huì)出現(xiàn)負(fù)數(shù)呢?
因?yàn)楫?dāng)三條線程同時(shí)調(diào)用"-(void)subMethod:(NSString *)stu;"方法時(shí);
發(fā)現(xiàn)那個(gè)時(shí)候的"numbers = 1" 所有都同時(shí)進(jìn)行了 減 1 的操作;
如果此處是對(duì)數(shù)組進(jìn)行取值的,就會(huì)出現(xiàn)崩潰
//可以通過 @synchronized(鎖死的對(duì)象){} 進(jìn)行
@synchronized ( self.models<這個(gè)地方只能傳OC對(duì)象類型> ) {
//在線程所內(nèi)對(duì)數(shù)據(jù)源進(jìn)行增加或減少的操作
}
//改寫后的減法操作
-(void)subMethod:(NSString *)stu{
while (1) {
@synchronized ( self) {
//在線程所內(nèi)對(duì)數(shù)據(jù)源進(jìn)行增加或減少的操作
if (self.numbers > 0) {
for (int i = 0; i< 100000; i++) {}
self.numbers -= 1;
NSLog(@"%@使用了減法后剩余:%d",stu,self.numbers);
}else{
NSLog(@"%@發(fā)現(xiàn)減數(shù)為0",stu);
break;
}
}
}
}
4.GCD線程?hào)艡趩栴}
#pragma mark 柵欄函數(shù)
-(void)method2{
dispatch_queue_t q = dispatch_queue_create("柵欄函數(shù)", DISPATCH_QUEUE_CONCURRENT);
//注意,柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
// dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(q, ^{NSLog(@"執(zhí)行任務(wù)一");});
dispatch_async(q, ^{ NSLog(@"執(zhí)行任務(wù)二");});
dispatch_barrier_async(q, ^{ NSLog(@"執(zhí)行柵欄任務(wù)");});
dispatch_async(q, ^{NSLog(@"執(zhí)行任務(wù)三");});
}
/*上面的方法,在多線程任務(wù)重,會(huì)限制性任務(wù)一和二,(一/二的先后順序由系統(tǒng)控制)
當(dāng)任務(wù)一和任務(wù)二執(zhí)行完成以后,系統(tǒng)會(huì)再執(zhí)行任務(wù)柵欄函數(shù)后的任務(wù);
*/
5.GCD快速遍歷數(shù)組
NSArray *characters = @[@"a",@"b",@"c",@"d",@"e"];
//普通的遍歷方式
for (NSString *C in characters) {
NSLog(@"%@--%@",C,[NSThread currentThread]);
}
//普通的遍歷方式會(huì)在本來的線程中進(jìn)行串行遍歷
//GCD遍歷方
dispatch_queue_t q = dispatch_queue_create("并發(fā)隊(duì)列", DISPATCH_QUEUE_CONCURRENT);
/*
參數(shù)1:遍歷的次數(shù),數(shù)組的count屬性
參數(shù)2:隊(duì)列 <只能用并發(fā)隊(duì)列,如果用主隊(duì)列就會(huì)線程死鎖,如果是串行隊(duì)列,那就不會(huì)開多個(gè)子線程>
參數(shù)3:遍歷的 index
*/
dispatch_apply(characters.count, q, ^(size_t i) {
NSString *c = characters[i];
NSLog(@"%@--%@",c,[NSThread currentThread]);
});
//使用GCD并發(fā)隊(duì)列遍歷,系統(tǒng)會(huì)開辟子線程對(duì)數(shù)組元素同時(shí)進(jìn)行遍歷,提升APP性能
5.GCD group 隊(duì)列組
GCD隊(duì)列組的可以用于監(jiān)聽隊(duì)列組中任務(wù)是否執(zhí)行完,執(zhí)行完后再執(zhí)行下一個(gè)操作
-(void)GCDgroupDemo{
//創(chuàng)建隊(duì)列
dispatch_queue_t q = dispatch_queue_create("group", DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建隊(duì)列組
dispatch_group_t g = dispatch_group_create();
dispatch_group_async(g, q, ^{
for (double i = 0; i<=1.0; i+=0.00002) {
dispatch_sync(dispatch_get_main_queue(), ^{
self.p1.progress = i;
});
}
});
//往隊(duì)列組里面添加隊(duì)列和任務(wù)
dispatch_group_async(g, q, ^{
for (double i = 0; i<=1.0; i+=0.00001) {
dispatch_sync(dispatch_get_main_queue(), ^{
self.p2.progress = i;
});
}
});
//監(jiān)聽隊(duì)列執(zhí)行完成情況
dispatch_group_notify(g, q, ^{
NSLog(@"%@",[NSThread currentThread]);
for (double i = 0; i<=1.0; i+=0.00002) {
dispatch_sync(dispatch_get_main_queue(), ^{
self.p3.progress = i;
});
}
});
NSOperation
1.NSOperation的基本概念
NSOperation 是蘋果封裝的一個(gè)對(duì)象類型的多線程方式;
NSOperation不需要用戶控制什么時(shí)候開線程執(zhí)行任務(wù)
NSOperation可以控制同一時(shí)間內(nèi)執(zhí)行的任務(wù)數(shù)量
2.NSOperationQueue的基本使用
使用NSOperation 和 NSOperationqueue 實(shí)現(xiàn)多線程的步驟
1.將需要執(zhí)行的任務(wù)封裝到NSOperation中
2.將NSOperation對(duì)象封添加NSOperationqueue中
3.系統(tǒng)自動(dòng)將NSOperationqueue中的NSOperation取出來
4.將NSOperation中封裝的任務(wù)放在線程中執(zhí)行
NSOperation的子類配合NSOperationQueue使用:
//1. NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
//2. NSBlockOperation
__weak typeof(self) weakself = self;
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{[weakself operationDemoMethod]}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{[weakself operationDemoMethod]}];
NSBlockOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{[weakself operationDemoMethod]}];
//NSOperationQueue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
[queue addOperation:op6];
3. NSOperation的線程依賴 和 監(jiān)聽
//1.封裝任務(wù)到NSOperation中
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:weakself selector:@selector(operationDemoMethod) object:nil];
//2.添加線程依賴 <依賴是可以跨隊(duì)列的,即: 不處于同一個(gè)NSOperationQueue中的NSOperation是可以使用依賴操作的>
[op2 addDependency:op1];
[op3 addDependency:op2];
/*
添加完這樣的線程依賴后,當(dāng)系統(tǒng)調(diào)取NSOperationQueue中的
任務(wù),就會(huì)按照依賴順序先執(zhí)行op1中的任務(wù),op1的任務(wù)執(zhí)行結(jié)
束以后,才會(huì)執(zhí)行op2中的任務(wù),同理,op3也依賴于op2
*/
//3.創(chuàng)建NSOperationQueue 并添加 NSOperation
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
//4.線程的監(jiān)聽
op2.completionBlock = ^{
//op2執(zhí)行完成后,想要實(shí)現(xiàn)的操作可以直接再這里調(diào)用
//這個(gè)方法和線程的依賴是差不多的
};
注意:不能出現(xiàn)循環(huán)依賴
[op2 addDependency:op1];
[op1 addDependency:op2];
//這么寫,程序不會(huì)崩潰,但是op1 & op2 都不會(huì)執(zhí)行
3. NSOperationQueue的暫停/回復(fù)/取消/最大并發(fā)數(shù)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
/*控制queue的同時(shí)并發(fā)線程數(shù)是:3;
這里不能等同于控制只開3條線程;
queue隊(duì)列可以開辟超過3條線程,但是同一時(shí)間內(nèi)只會(huì)并發(fā)執(zhí)行3條
*/
queue.maxConcurrentOperationCount = 3;
//暫停隊(duì)列
/*
這里假設(shè)一個(gè)場(chǎng)景:
我們往隊(duì)列中添加了10個(gè)任務(wù),
隊(duì)列的最大并發(fā)數(shù)為:3個(gè)
當(dāng)隊(duì)列開始執(zhí)行任務(wù)的時(shí)候,但是又沒有執(zhí)行完成的時(shí)候,想暫停
那么我們可以使用一下方法實(shí)現(xiàn)NSOperationQueue的暫停
*/
[queue setSuspended:YES];
//同理,我們想要恢復(fù)NSOperationQueue的時(shí)候,只需要設(shè)置NO
[queue setSuspended:NO];
//注意:當(dāng)點(diǎn)擊暫停的時(shí)候,已經(jīng)開始執(zhí)行的任務(wù)會(huì)執(zhí)行完成
//NSOperationQueue的暫停原理是:停止調(diào)取queue中未開始執(zhí)行的任務(wù)
//NSOperationQueue 取消任務(wù)
[queue cancelAllOperations];
//被取消的NSOperationQueue 是不可再恢復(fù)執(zhí)行的,
4. NSOperationQueue 回到主線程操作
通常我們開辟子線程都是用來執(zhí)行耗時(shí)操作獲取數(shù)據(jù)的,一旦數(shù)據(jù)獲取,我們需要刷新UI展示給用戶看,這個(gè)時(shí)候需要回到主線程刷新UI
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//1.執(zhí)行耗時(shí)操作獲取數(shù)據(jù)
.....
//2.獲得數(shù)據(jù)以后,回到主線程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//在此處已經(jīng)回到主線程,可以直接執(zhí)行刷新UI的代碼
}];
}];
多線程的幾種情況已經(jīng)總結(jié)完成
如果您巧合看到這篇文章,想要交流可以直接留言,若有不足之處也請(qǐng)留言之處,十分感激;