基礎(chǔ)概念:
大部分現(xiàn)代操作系統(tǒng)都支持執(zhí)行線程的概念.每個進程可以包含多個線程,它們可以同時運行.如果只有一個處理器核心,操作系統(tǒng)將在所有執(zhí)行線程之間切換,非常類似于在所有執(zhí)行進程之間切換.如果擁有多個核心,線程將像進程一樣,分散到幾個核上去執(zhí)行.比如:
單核并發(fā)執(zhí)行兩個線程相當(dāng)于 一個廚師同時炒兩盤菜,廚師炒一會菜A,在炒一會兒菜B,只不過來回切換的速度特別特別快,感覺上廚師同時在炒兩盤菜.其實本質(zhì)上還是一個個炒. 這里涉及到一個優(yōu)先級的概念 如果線程A的優(yōu)先級高,線程B的優(yōu)先級低, 那么廚師的注意力就會更集中在炒菜A,菜B只是時不時的照看一下.而且也能聯(lián)想到線程不是越多越好.畢竟廚師也有忙不過來的時候,嘿嘿
多線程并發(fā)執(zhí)行,其實是CPU快速地在多條線程之間調(diào)度.
開啟線程需要占用一定的內(nèi)存空間,如果開啟大量的線程,會占用大量的內(nèi)存空間.降低程序的性能.線程越多,CPU在調(diào)度線程上的開銷也就越大.
一個進程中的所有線程共享可執(zhí)行程序代碼和全局數(shù)據(jù)(一個廚房里的所有廚師可以共用廚房里的調(diào)料,電飯鍋等..).每個線程也可以擁有一些獨有的數(shù)據(jù)(當(dāng)然也允許廚師有專屬自己的裝備了~).
線程安全:處理線程可以使用一種稱為互斥量(mutex)或鎖的特殊結(jié)構(gòu)來確保特定的代碼塊無法一次被多個線程運行.(當(dāng)兩個廚師需要用同一個烤箱時,需要溝通下 廚師X先用鎖把烤箱鎖住,廚師Y一看烤箱鎖住了,就知道有人在用了,等廚師X用完,要把烤箱的鎖解除,這樣廚師Y就知道X用完了.)
線程的串行:如果在一個線程中執(zhí)行多個任務(wù)(A,B,C),只能一個個的按順序執(zhí)行這些任務(wù).
在iOS中 多線程有4種
- pthread
- NSThread
- GCD
- NSOperationQueue
圖片摘自小馬哥的多線程PPT中.
一 pthread
pthread是基于C語言的,對此了解的不多,只知道有這么個東西.其實pthread真的用的不多.
void *run(void *data)
{
for (int i = 0; i<10000; i++) {
NSLog(@"currentThread----%d-----%@", i, [NSThread currentThread]);
}
return NULL;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 創(chuàng)建線程
pthread_t myRestrict;
pthread_create(&myRestrict, NULL, run, NULL);
}
二 NSThread
NSThread 還算是比較常見,但也不是很常用,NSThread有3種創(chuàng)建線程的方法
一個NSThread對象就代表一條線程.
[NSThread currentThread];
// 主線程
[NSThread mainThread];
// 是否是主線程
[NSThread isMainThread];
/**
* 創(chuàng)建線程的方式1
*/
- (void)createThread1
{
// 創(chuàng)建線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"http://b.png"];
thread.name = @"下載線程";
// 啟動線程(調(diào)用self的download方法)
[thread start];
}
/**
* 創(chuàng)建線程的方式2
*/
- (void)createThread2
{
[NSThread detachNewThreadSelector:@selector(download:) toTarget:self withObject:@"parameterOfDownload"];
}
/**
* 創(chuàng)建線程的方式3
*/
- (void)createThread3
{
// 這2個不會創(chuàng)建線程真友,在當(dāng)前線程中執(zhí)行
// [self performSelector:@selector(download:) withObject:@"http://c.gif"];
// [self download:@"http://c.gif"];
[self performSelectorInBackground:@selector(download:) withObject:@"http://c.gif"];
}
線程的狀態(tài)(iOS中GCD已經(jīng)為我們管理了這些,其實不需要知道):
當(dāng)start一個線程之后,線程會是就緒狀態(tài)(可被調(diào)度),等CPU調(diào)度到它了,就變成運行狀態(tài),調(diào)度一會CPU會切換到可調(diào)度線程池的其他線程去.那當(dāng)前線程會回到就緒狀態(tài),等待下一次被調(diào)度.如果當(dāng)前線程sleep了,說明當(dāng)前線程從可調(diào)度線程池里移除了,sleep之后還會回到線程池里,也就是就緒狀態(tài).線程任務(wù)執(zhí)行完畢,異常/強制退出時 線程會死亡,即從內(nèi)存中消失.
線程同步:
為了防止多個線程搶奪同一個資源造成的數(shù)據(jù)安全問題.
給代碼加一個互斥鎖(同步鎖)@synchronized(鎖對象) {
被鎖住的代碼
}
注意: 這個鎖對象必須是同一個對象
線程間的通信:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self performSelectorInBackground:@selector(download) withObject:nil];
}
/**
* 下載圖片
*/
- (void)download
{
NSLog(@"download---%@", [NSThread currentThread]);
// 1.圖片地址
NSString *urlStr = @"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg";
NSURL *url = [NSURL URLWithString:urlStr];
// 2.根據(jù)地址下載圖片的二進制數(shù)據(jù)(這句代碼最耗時)
NSLog(@"---begin");
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(@"---end");
// 3.設(shè)置圖片
UIImage *image = [UIImage imageWithData:data];
// 4.回到主線程蜂厅,刷新UI界面(為了線程安全)
[self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO];
NSLog(@"-----done----");
}
- (void)downloadFinished:(UIImage *)image
{
self.imageView.image = image;
NSLog(@"downloadFinished---%@", [NSThread currentThread]);
}