進程和線程的區(qū)別
1.進程是具有一定獨立功能的程序關于某個數據集合上的一次運行活動偷崩,它是系統(tǒng)資源分配的基本單位。線程是處理機調度的最小單位帚呼。
2.進程有自己獨立的地址空間;進程至少有一個線程掏缎,它們共享進程的地址空間,同一個進程內的線程共享進程的資源煤杀。
多線程是指從軟件或者硬件上實現多個線程并發(fā)執(zhí)行的技術眷蜈。具有多線程能力的計算機因有硬件支持而能夠在同一時間執(zhí)行多于一個線程,進而提升整體處理性能沈自。其本質上并不是同時執(zhí)行多個程序酌儒,而是操作系統(tǒng)根據合理的算法在較短的時間內完成各個線程之間的切換,每個線程執(zhí)行一段時間后枯途,切換到下一個線程執(zhí)行忌怎。
多線程的優(yōu)點:能適當提高程序的執(zhí)行效率;能適當提高資源利用率(CPU柔袁、內存利用率)呆躲。
多線程的缺點:開啟線程需要占用一定的內存空間(默認情況下,主線程占用1M捶索,子線程占用512KB)插掂,如果開啟大量的線程,會占用大量的內存空間,降低程序的性能辅甥;線程越多酝润,CPU在調度線程上的開銷就越大;程序設計更加復雜:比如線程之間的通信璃弄、多線程的數據共享要销。
使用線程可以把占據時間長的程序中的任務放到后臺去處理用戶界面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發(fā)某些事件的處理夏块,可以彈出一個進度條來顯示處理的進度程序的運行速度可能加快在一些等待的任務實現上如用戶輸入疏咐、文件讀寫和網絡收發(fā)數據等。
iOS中實現多線程的方式
NSThread
輕量級別的多線程技術脐供,需要我們手動來管理線程浑塞。提供的方法比較少,例如串行政己,并發(fā)執(zhí)行這些實現起來相當困難酌壕。開辟子線程的方法有兩種,一個是初始化需要手動開啟歇由,也就是調用start方法卵牍,并且有返回值,返回的就是NSThread對象沦泌,可以設置線程名稱糊昙,設置線程的優(yōu)先級等一些參數。另一種是便利構造器的方法開辟子線程無返回值赦肃,會自動啟動線程溅蛉。不需要手動調用start方法。
//通過NSThread開辟子線程
//object:這個是回調方法的參數
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(testThread) object:nil];
thread.name = @"我是第二條線程";
//調整thread的優(yōu)先級他宛,線程權限的范圍值為0~1船侧,值越大優(yōu)先級越高,默認值為0.5厅各。由于是概率镜撩,所以并不能很準確的實現我們先要的執(zhí)行順序。
thread.threadPriority = 0.8;
//當使用初始化方法創(chuàng)建的子線程队塘,需要start啟動
[thread start];
//取消當前已經啟動的線程
// [thread1 cancel];
線程的回調方法
- (void)testThread
{
//如果子線程是我們手動開辟的袁梗,那么就需要我們來回收它所占用的資源。
@autoreleasepool {
NSLog(@"%@",[NSThread currentThread]);
double sum = 0;
for (double i = 0; i < 500000000; i++) {
sum += i;
}
//[NSThread currentThread]獲取當前線程
NSLog(@"sum------%f------%@",sum,[NSThread currentThread]);
}
NSObject
只要是NSObject的子類或者對象都可以通過調用方法進入子線程和主線程憔古,其實這些方法所開辟的子線程也是NSThread的另一種體現方式遮怜。
開辟子線程:
[self performSelectorInBackground:@selector(aaa) withObject:nil];
進入主線程:
[self performSelectorOnMainThread:@selector(bbb) withObject:nil waitUntilDone:YES];
NSOperationQueue
它使OC級別的封裝,是將一組事件添加到隊列中鸿市,如果想讓這組事件在主線程中執(zhí)行锯梁,那么就需要主隊列[NSOperationQueue mainQueue];如果想要將一組事件在子線程中執(zhí)行那么就需要其他隊列[[NSOperation alloc] init];NSOperation就是事件即碗,它本身是一個抽象類,如果需要實現具體操作陌凳,需要它的兩個子類:NSInvocationOperation和NSBlockOperation剥懒;事件本身和線程無關,只是看你將它加到那種隊列中或者將事件放入主線程中合敦。如果在隊列中想要使得事件順序執(zhí)行初橘,需要給事件添加依賴關系,添加依賴關系的時候兩個事件不能互為依賴充岛。也可以設置事件的優(yōu)先級來提高它先執(zhí)行的概率保檐,但是不準確。還可以設置隊列的最大并發(fā)執(zhí)行的個數崔梗,來使得事件順序執(zhí)行展东。NSThread需要程序員手動管理,NSOperationQueue會自動管理線程炒俱。
- (void)operationQueue
{
//這個類只是執(zhí)行一個操作,本身和線程無關爪膊,也就是把對象放到哪個線程之中权悟,它就在哪個線程中執(zhí)行。
NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAction) object:nil];
//該事件需要手動啟動
// [invocation start];
//通過block方式來增加事件
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block------%@",[NSThread currentThread]);
}];
//block方式的operation可以增加一組額外的block事件,通過這種方式為blockOperation添加的block事件的執(zhí)行順序無法掌控推盛,所在的線程也無法掌控峦阁。
for (int i = 0; i < 5; i++) {
[blockOperation addExecutionBlock:^{
NSLog(@"execuBlock------%@",[NSThread currentThread]);
}];
}
//當所有的block事件都執(zhí)行完成,我們可以讓它發(fā)出通知耘成,告訴我們所有事件都執(zhí)行完成
blockOperation.completionBlock = ^{
NSLog(@"不管上面怎么執(zhí)行榔昔,它肯定是最后一個執(zhí)行的");
NSLog(@"%@",[NSThread currentThread]);
};
//只要是事件都需要手動啟動
// [blockOperation start];
//事件不具備開辟線程的能力,所以我們需要配合queue來使用它瘪菌,queue就是隊列撒会,其實隊列就是用來處理一組時間,事件加到隊列中师妙,就不需要手動啟動诵肛,也不用我們來關心它開辟線程之后的消耗,這些事情都是隊列幫助我們處理默穴。隊列一般分為主隊列和其他隊列
//其他隊列怔檩,可以將時間在除主線程的其他線程中處理,而且執(zhí)行順序不定(并發(fā)執(zhí)行)通過設置最大并發(fā)數或者事件依賴蓄诽,就可以將一組事件在子線程中順序執(zhí)行薛训,達到串行的目的。
// NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//主隊列仑氛,是將一組時間在主線程中執(zhí)行乙埃,不用設置任何屬性闸英,一組事件都會順序執(zhí)行。
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//我們需要按照順序執(zhí)行一組事件膊爪,有兩種方式
//第一種自阱,設置該隊列最大并發(fā)執(zhí)行事件的個數,該屬性默認值為-1米酬,該隊列中有多少個事件沛豌,就并發(fā)執(zhí)行多少個。如果并發(fā)事件設置為1赃额,那就是一次只執(zhí)行一個事件
// queue.maxConcurrentOperationCount = 1;
//第二種方式加派,就是添加事件依賴,事件依賴意思就是跳芳,當一個時間執(zhí)行完畢才會執(zhí)行另一個事件芍锦,這里就是先執(zhí)行blockOperation,在執(zhí)行invocation
[blockOperation addDependency:invocation];
//給隊列中添加事件
[queue addOperation:invocation];
[queue addOperation:blockOperation];
}
GCD
GCD效率比operationQueue要高一些飞盆,功能更強大娄琉,目前有替代其他多線程方式的趨勢,它處理事件主要是通過隊列來執(zhí)行吓歇。分為兩種隊列孽水,一種是串行,一種是并行城看。系統(tǒng)提供給我們的一種是全局隊列女气,一種是主隊列。添加事件的函數為:dispatch_async();一般我們都是用異步添加時間测柠,最重要的原因是它不會阻塞當前線程炼鞠。全局隊列中所添加的異步時間肯定都是在子線程中的。主隊列中添加的事件不管是異步還是同步都是在主線程中轰胁。
同步函數不具備開啟線程的能力谒主,無論是什么隊列都不會開啟線程;異步函數具備開啟線程的能力赃阀,開啟幾條線程由隊列決定(串行隊列只會開啟一條新的線程瘩将,并發(fā)隊列會開啟多條線程)。
同步函數:
(1)并發(fā)隊列:不會開線程
(2)串行隊列:不會開線程
異步函數:
(1)并發(fā)隊列:能開啟N條線程
(2)串行隊列:開啟1條線程
補充:凡是函數中凹耙,各種函數名中帶有create\copy\new\retain等字眼姿现,都需要在不需要使用這個數據的時候進行release。
GCD的數據類型在ARC的環(huán)境下不需要再做release肖抱。
異步函數具備開線程的能力备典,但不一定會開線程。
串行隊列
//創(chuàng)建一個串行隊列
//dispatch_queue_create函數是用來創(chuàng)建隊列使用意述,第一個參數為該隊列的標簽缘缚;第二個參數為該隊列類型,通常設置為NULL
dispatch_queue_t serialQueue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
//給該隊列添加事件
//第一個參數為該事件所在的隊列广凸;第二個參數為block,該時間所要做的處理
dispatch_async(serialQueue, ^{
NSLog(@"洛洛出生了---%@",[NSThread currentThread]);
});
并行隊列
//創(chuàng)建一個并行隊列
dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"我是第1個---%@",[NSThread currentThread]);
});
//此函數會阻塞當前線程潮针,對主線程無影響
dispatch_barrier_async(queue, ^{
NSLog(@"我在執(zhí)行---%@",[NSThread currentThread]);
});
系統(tǒng)提供的全局隊列
- (void)globalQueue
{
//得到系統(tǒng)的提供的全局隊列
//dispatch_get_global_queue(0, 0);
//第一個參數為該全局隊列的優(yōu)先級
//第二個參數暫時沒用,是系統(tǒng)為了后面擴展來使用倚喂,在將來的某一天會使用到每篷。直接賦值為0就可以
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
NSLog(@"正在網絡下載或者一些其他耗時的操作");
//耗時操作完成之后為主線程更新UI,GCD回主線程方式
//要得到主隊列端圈,和operationQueue中的mainQueue是一樣的概念
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
//在此處進行UI刷新
NSLog(@"mainQueue---%@",[NSThread currentThread]);
});
});
}
GCD中代碼的延時執(zhí)行
//第一個參數是說從什么時候開始計時
//多少秒之后執(zhí)行block中的操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"5秒之后執(zhí)行");
});
NSObject代碼的延時執(zhí)行
[self performSelector:@selector(afterAction:) withObject:@"五秒之后執(zhí)行" afterDelay:5];