1. 什么是程序、進程、線程
1.1 程序:
由源代碼生成的可執(zhí)行應(yīng)用。(例如:QQ.APP)
1.2 進程:
一個正在運行的程序可以看做一個進程。(例如:正在運行的QQ就是一個進程),進程擁有獨立運行所需的全部資源。
1.3 線程:
程序中獨立運行的代碼段。(例如:接收QQ消息的代碼)
一個進程是由一或多個線程組成。進程只負責資源的調(diào)度和分配,線程才是程序真正的執(zhí)行單元,負責代碼的執(zhí)行。
2. 單線程與多線程有什么區(qū)別
2.1單線程
每個正在運行的程序(即進程),至少包含一個線程,這個線程叫主線程。
主線程在程序啟動時被創(chuàng)建,用于執(zhí)行main函數(shù)。
只有一個主線程的程序,稱作單線程程序。
主線程負責執(zhí)行程序的所有代碼(UI展現(xiàn)以及刷新,網(wǎng)絡(luò)請求,本地存儲等等)。這些代碼只能順序執(zhí)行,無法并發(fā)執(zhí)行。
2.2多線程
擁有多個線程的程序,稱作多線程程序。
iOS允許用戶自己開辟新的線程,相對于主線程來講,這些線程,稱作子線程。
可以根據(jù)需要開辟若干子線程
子線程和主線程是 都是 獨立的運行單元,各自的執(zhí)行互不影響,因此能夠并發(fā)執(zhí)行。
2.3區(qū)別
單線程程序:只有一個線程,代碼順序執(zhí)行,容易出現(xiàn)代碼阻塞(頁面假死)。
多線程程序:有多個線程,線程間獨立運行,能有效的避免代碼阻塞,并且提高程序的運行性能。
注意:iOS中關(guān)于UI的添加和刷新必須在主線程中操作。
3. iOS多線程實現(xiàn)種類
主要由四種:NSThread、NSoperationQueue、NSobject、GCD
1.1輕量級別的多線程技術(shù),
需要我們手動管理線程,還有提供的方法比較少,例如:串行,并發(fā)執(zhí)行這些實現(xiàn)起來相當困難,開辟子線程的方法有兩種,這個需要我盟手動開啟線程,也就是start方法,并且有返回值,返回值就是NSThread對象,可以設(shè)置線程名稱,設(shè)置線程的權(quán)限的等級一些操作參數(shù)。另一個是便利構(gòu)造器的方法開辟子線程,無返回值,會自動啟動線程,不需要手動調(diào)用start方法。
#pragma mark -- NSThread 開辟子線程
// NSThread是我們自己手動開辟的子線程,如果使用的是初始化方式就需要我們自己是釋放,如果使用的是便利構(gòu)造器方式它就會自動啟動,只要是我們手動開辟的線程,都需要我夢自己管理該線程,不只是啟動,還有該線程使用完畢后的資源回收,所以在NSThread的回調(diào)方法中需要加入自動釋放池來回收資源
- (void)threadInfo{
// object:這個是回調(diào)方法的參數(shù);
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(testThread:) object:@"我是參數(shù)"];
// 當使用初始化方法出來的主線程需要start啟動
[thread start];
// NSLog(@"我是逗比");
// 可以為開辟的子線程起名字
thread.name = @"我是第二條線程";
// 調(diào)整Thread的權(quán)限 線程權(quán)限的范圍值為0 ~ 1 。越大權(quán)限越高,先執(zhí)行的概率就會越高,由于是概率,所以并不能很準確的的實現(xiàn)我們想要的執(zhí)行順序,默認值是0.5
thread.threadPriority = 1;
// 取消當前已經(jīng)啟動的線程
// [thread cancel];
// 通過遍歷構(gòu)造器開辟子線程
// [NSThread detachNewThreadSelector:@selector(testThread:) toTarget:self withObject:@"便利構(gòu)造器方式"];
}
1.2回調(diào)方法
如果子線程是我們手動開辟的,那么就需要我們來管理它運行所造成的資源回收
- (void)testThread:(NSString *)testString{
// 如果子線程是我們手動開辟的,那么就需要我們來管理它運行所造成的資源回收
@autoreleasepool {
NSLog(@"參數(shù)----%@",testString);
NSLog(@"testThread -- %@",[NSThread currentThread]);
double sum = 0;
for (int i = 1; i < 635500000; i ++) {
sum += i;
}
NSLog(@"sum = %f---- %@",sum,[NSThread currentThread]);
// 回到主線程 所有的NSObject對象或者NSobject的子類都有該方法,他是NSthread的另一種體現(xiàn)方式
// waitUntilDone:是否將該回調(diào)方法執(zhí)行完在執(zhí)行后面的代碼,如果為YES:就必須等回調(diào)方法執(zhí)行完成之后才能執(zhí)行后面的代碼,說白了就是阻塞當前的線程,如果是NO:就是不等回調(diào)方法結(jié)束。不會阻塞當前線程
// 回到主線程
[self performSelectorOnMainThread:@selector(backMainThread) withObject:nil waitUntilDone:YES];
}
}
- (void)backMainThread{
NSLog(@"回到主線程");
}
2.NSObject
只要是NSObject的子類或者對象都可以通過調(diào)用方法進入子線程和主線程,其實這些方法所開辟的子線程也是NSThread的另一種體現(xiàn)方式。
開辟子線程:
[self performSelectorInBackground:@selector(aaa) withObject:nil];
進入主線程:
[self performSelectorOnMainThread:@selector(bbb) withObject:nil
waitUntilDone:YES];
3.3.NSOperationQueue:他是將一組事件添加到隊列中,如果想讓這組事件是在主線程中執(zhí)行,那么就需要主隊列[NSOperationQueue mainQueue];如果想將一組事件在子線程中執(zhí)行那么就需要其他隊列[[NSOperationQueue alloc] init];NSOperationQueue就是事件,它本身是一個抽象類,如果需要實現(xiàn)具體操作,需要他的兩個子類
NSInvocationOperation和NSBlockOperation;事件本身與線程無關(guān),知識看你將他加到那種隊列中。如果加到隊列中想要是的時間順序執(zhí)行,需要給事件添加依賴關(guān)系,添加依賴關(guān)系的時候兩個事件不能互為依賴。也可以設(shè)置時間的優(yōu)先級來提高它先執(zhí)行的概率,但是不準確,還可以設(shè)置隊列最大并發(fā)行數(shù),來是事件順序執(zhí)行。
- (void)invocationOperationInfo{
// NSInvocationOperation 這個類只執(zhí)行一個操作,本身與線程無關(guān),意思就是,你把該類對象放到哪個線程里面,他就在那個線程中執(zhí)行
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector( invocationOperationAction) object:nil];
// 改事件需手動執(zhí)行
// [invocationOperation start];
// 通過Block方式增加一個事件
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block --- %@",[NSThread currentThread]);
}];
// block方式的operation可以增加一組額外的block事件,通過這種方式的blockOperation添加的block事件順序無法掌握沒所以線程無法掌控
for (int i = 0; i < 5; i ++) {
[blockOperation addExecutionBlock:^{
NSLog(@"execuBlock %@",[NSThread currentThread]);
}];
}
// 當所有的block事件都執(zhí)行完了,我們就可以讓他發(fā)出通知,告訴我們所有的事件都執(zhí)行完了
blockOperation.completionBlock = ^{
NSLog(@"不管上面的小弟怎么鬧,我是最后一個");
NSLog(@"_____%@",[NSThread currentThread]);
};
// 主隊列 是將一組事件在主線程中執(zhí)行,不用設(shè)置任何屬性,一組事件都會順序執(zhí)行
// 我們需要按照順序來執(zhí)行一組事件,轉(zhuǎn)讓個時候該怎么辦? 有兩種方式
NSOperationQueue *queue = [NSOperationQueue mainQueue];
// 第一種:設(shè)置隊列最大并發(fā)執(zhí)行事件的?個數(shù),該屬性默認值是-1;意思是:該隊列中有多少個事件,就并發(fā)執(zhí)行多少個,如果設(shè)置并發(fā)事件設(shè)置為1,那就是一次只執(zhí)行一個事件,
// queue.maxConcurrentOperationCount = 1;
// 第二種:通過添加事件依賴,事件依賴的意思就是說:當?shù)匾粋€事件執(zhí)行完畢之后才執(zhí)行另一個事件,在這里就是先執(zhí)行invocationOperation,在執(zhí)行blockOperation,
[blockOperation addDependency:invocationOperation];
// 給隊列中增加事件
[queue addOperation:invocationOperation];
[queue addOperation:blockOperation];
}
4.GCD:
GCD效率比operationQueue要高一些,功能更強一些,目前有替代其他多線程的趨勢,他處理時間主要通過隊列來執(zhí)行,分為兩種隊列,一種是串行,另一中是并行,系統(tǒng)提供給我們的是全局隊列,一種是主隊列,添加時間函數(shù)為dispatch_async();一般我們都用一部添加事件,最重要的原因是他不會阻塞當前線程,全局隊列中所添加的異步事件坑定都是子線程中的,主隊列中添加事件不管同步還是異步都在主函數(shù)中運行;?。。?/h5>
一定記住會主線程要刷新UI原因是:1.iOS中為了效率更高,多數(shù)線程是沒有安全保證的,在子線程中刷新UI有可能會遇到不可預(yù)知的錯誤,2.在子線程中刷新UI,只有當前線程執(zhí)行完成,才會刷新UI,和可能造成UI刷新不及時,影響用戶體驗,而且一般只有主線程才有UI刷新功能
創(chuàng)建一個串行隊列
- (void)serialQueuueCuanXing{
// 創(chuàng)建一個串行隊列
// dispatch_queue_create 函數(shù)是用來創(chuàng)建隊列使用,第一個參數(shù)為該隊列的標簽,第二個參數(shù)為該隊列類型
dispatch_queue_t serialQueue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
// 給該隊列添加事件
// 第一個參數(shù)為該事件所在的隊列,第二個參數(shù)為Block,該事件索要做的處理
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸出生了---%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸會穿衣服了---%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸掛墻上了----%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行完了-- %@",[NSThread currentThread]);
}
創(chuàng)建一個并行隊列
// 并行隊列
- (void)seriaQueueBingXing{
dispatch_queue_t seriaQueue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(seriaQueue, ^{
NSLog(@"我是第一---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第二---%@",[NSThread currentThread]);
});
// 此函數(shù)會阻塞當前線程,對主線程無影響。
dispatch_barrier_async(seriaQueue, ^{
NSLog(@"我正在執(zhí)行---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第三---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第四---%@",[NSThread currentThread]);
});
}
5.系統(tǒng)提供的全局隊列
- (void)globalQueue{
// 上面都是我們自主創(chuàng)建的隊列,一般使用中,我們都不會自己創(chuàng)建,而是使用系統(tǒng)提供的隊列,系統(tǒng)提供的隊列有全局隊列,在此隊列中可以添加多個異步事件,并發(fā)執(zhí)行
// 一般我們都是使用系統(tǒng)提供的們不用自己創(chuàng)建
// 第一個參數(shù)為該全局隊列的優(yōu)先級
// 第二個參數(shù)暫時沒用,是系統(tǒng)為后面擴展來使用的,在將來的某一天使用到,直接賦0就可以了
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
NSLog(@"正在網(wǎng)絡(luò)下載或者其他一些耗時操作");
// 耗時操作完成之后回主線程更新UI,GCD回主線程方式
// 要得到主隊列,和operationQueue中的mainQueue是一樣的概念
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
// 在此處進行UI刷新
NSLog(@"mainQueue -- %@",[NSThread currentThread]);
});
});
}
- (void)serialQueuueCuanXing{
// 創(chuàng)建一個串行隊列
// dispatch_queue_create 函數(shù)是用來創(chuàng)建隊列使用,第一個參數(shù)為該隊列的標簽,第二個參數(shù)為該隊列類型
dispatch_queue_t serialQueue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
// 給該隊列添加事件
// 第一個參數(shù)為該事件所在的隊列,第二個參數(shù)為Block,該事件索要做的處理
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸出生了---%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸會穿衣服了---%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸掛墻上了----%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行完了-- %@",[NSThread currentThread]);
}
// 并行隊列
- (void)seriaQueueBingXing{
dispatch_queue_t seriaQueue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(seriaQueue, ^{
NSLog(@"我是第一---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第二---%@",[NSThread currentThread]);
});
// 此函數(shù)會阻塞當前線程,對主線程無影響。
dispatch_barrier_async(seriaQueue, ^{
NSLog(@"我正在執(zhí)行---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第三---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第四---%@",[NSThread currentThread]);
});
}
- (void)globalQueue{
// 上面都是我們自主創(chuàng)建的隊列,一般使用中,我們都不會自己創(chuàng)建,而是使用系統(tǒng)提供的隊列,系統(tǒng)提供的隊列有全局隊列,在此隊列中可以添加多個異步事件,并發(fā)執(zhí)行
// 一般我們都是使用系統(tǒng)提供的們不用自己創(chuàng)建
// 第一個參數(shù)為該全局隊列的優(yōu)先級
// 第二個參數(shù)暫時沒用,是系統(tǒng)為后面擴展來使用的,在將來的某一天使用到,直接賦0就可以了
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
NSLog(@"正在網(wǎng)絡(luò)下載或者其他一些耗時操作");
// 耗時操作完成之后回主線程更新UI,GCD回主線程方式
// 要得到主隊列,和operationQueue中的mainQueue是一樣的概念
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
// 在此處進行UI刷新
NSLog(@"mainQueue -- %@",[NSThread currentThread]);
});
});
}