Pthread
使用pthread必須盜用頭文件#import <pthread.h>
可以使用[NSThread currentThread]
來(lái)獲取當(dāng)前在哪條線程上面 num = 1為主線程
創(chuàng)建線程
pthread_t myRestrict;
pthread_create(&myRestrict, NULL, run, NULL);run為一個(gè)void的函數(shù)
NSThread
主線程相關(guān)用法
+ (NSThread *)mainThread; // 獲得主線程
- (BOOL)isMainThread; // 是否為主線程
+ (BOOL)isMainThread; // 是否為主線程
其他用法
//獲得當(dāng)前線程
NSThread *current = [NSThread currentThread];
//線程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
創(chuàng)建線程
-
第一種方式
NSThread *thread = [NSThread alloc]initWithTarget:self selector:@selector(download:) object:@"哈哈"];//創(chuàng)建一個(gè)線程去調(diào)用download:方法并把哈哈傳給這個(gè)方法,也可以不傳值乌奇,設(shè)置為nil即可 thread.name = @"下載線程";//可以設(shè)置線程的名稱则披,設(shè)置與否不會(huì)造成什么影響的 [thread start];//啟動(dòng)線程蓖墅,必須加上儒飒,不然無(wú)法開(kāi)啟線程
-
第二種方式
[NSThread detachNewThreadSelector:@selector(download:) toTarget:self withObject:@"http://a.jpg"];
-
第三種方式(隱式創(chuàng)建)
[self performSelectorInBackground:@selector(download:) withObject:@"http://c.gif"]; [self performSelector:@selector(download:) withObject:@"http://c.gif"];(不會(huì)開(kāi)辟新線程) [self download:@"http://c.gif"];(不會(huì)開(kāi)辟新線程)
第二種和第三種創(chuàng)建的優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):簡(jiǎn)單快捷
- 缺點(diǎn):無(wú)法對(duì)線程進(jìn)行更詳細(xì)的設(shè)置
自定義NSThread
當(dāng)我們想監(jiān)聽(tīng)NSThread是否被釋放以及我們想自己搞一個(gè)自己Thread眼姐,這時(shí)候我們可以繼承自NSThread實(shí)現(xiàn)自己的Thread,有一個(gè)方法是需要實(shí)現(xiàn)的赏迟,main這個(gè)方法屡贺,我們需要做的事情可以放到這里面
線程狀態(tài)
一般來(lái)說(shuō)有以下幾種
- 新建NEW
- 就緒Runnable
- 運(yùn)行Running
- 阻塞Blocked
- 死亡Dead
狀態(tài)之間的切換見(jiàn)下圖所示:
CPU在同一時(shí)間內(nèi)只能執(zhí)行一條多線程,并發(fā)是CPU調(diào)度任務(wù)才會(huì)造成多線程是同時(shí)執(zhí)行的(因?yàn)檎{(diào)度事件非常短)
控制線程的狀態(tài)
//啟動(dòng)線程
- (void)start;
// 進(jìn)入就緒狀態(tài) -> 運(yùn)行狀態(tài)锌杀。當(dāng)線程任務(wù)執(zhí)行完畢甩栈,自動(dòng)進(jìn)入死亡狀態(tài)
//阻塞(暫停)線程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 進(jìn)入阻塞狀態(tài)
//強(qiáng)制停止線程
+ (void)exit;
// 進(jìn)入死亡狀態(tài)
注意:一旦線程停止(死亡)了,就不能再次開(kāi)啟任務(wù)
多線程的安全隱患
- 資源共享
- 1塊資源可能會(huì)被多個(gè)線程共享糕再,也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源
- 比如多個(gè)線程訪問(wèn)同一個(gè)對(duì)象量没、同一個(gè)變量、同一個(gè)文件
- 當(dāng)多個(gè)線程訪問(wèn)同一塊資源時(shí)突想,很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題(現(xiàn)實(shí)生活中有買車票和銀行存取錢等)
存錢取錢殴蹄,內(nèi)容見(jiàn)下圖
網(wǎng)上用的最多的一張圖
互斥鎖(@synchronized)
- 互斥鎖使用格式
@synchronized(鎖對(duì)象) { // 需要鎖定的代碼 }
注意:鎖定1份代碼只用1把鎖,用多把鎖是無(wú)效的
- 互斥鎖的優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
- 缺點(diǎn):需要消耗大量的CPU資源
- 互斥鎖使用前提是多條線程搶奪同一塊資源
- 相關(guān)專業(yè)術(shù)語(yǔ):線程同步
- 線程同步的意思是:多條線程在同一條線上執(zhí)行(按順序地執(zhí)行任務(wù))
- 互斥鎖猾担,就是使用了線程同步技術(shù)
火車票以及銀行取錢等情況下需要使用互斥鎖來(lái)對(duì)線程進(jìn)行加鎖袭灯,避免造成混亂等情況
比如三個(gè)人進(jìn)行買火車票,如果不用互斥鎖的話那么一張票可能被兩個(gè)人或者三個(gè)人同時(shí)買到绑嘹,顯然這是不符合邏輯的稽荧,每張票只能允許同一個(gè)人購(gòu)買。這時(shí)候就需要用到互斥鎖來(lái)對(duì)火車票進(jìn)行加鎖了工腋,如果某一張火車票被人購(gòu)買了姨丈,那么別人就無(wú)法繼續(xù)買這張火車票了。
- (void)saleTicket
{
while (1) {
// ()小括號(hào)里面放的是鎖對(duì)象(一般self即可擅腰,不要重復(fù)生成對(duì)象)
@synchronized(self) { // 開(kāi)始加鎖
int count = self.leftTicketCount;
if (count > 0) {
[NSThread sleepForTimeInterval:0.05];
self.leftTicketCount = count - 1;
NSLog(@"%@賣了一張票, 剩余%d張票", [NSThread currentThread].name, self.leftTicketCount);
} else {
return; // 退出循環(huán)
}
} // 解鎖
}
}
原子和非原子屬性
- OC在定義屬性時(shí)有nonatomic和atomic兩種選擇
- atomic:原子屬性蟋恬,為setter方法加鎖(默認(rèn)就是atomic)
- nonatomic:非原子屬性,不會(huì)為setter方法加鎖趁冈,一般都用nonatomic歼争,這樣性能會(huì)稍微好點(diǎn)
- nonatomic和atomic對(duì)比
- atomic:線程安全,需要消耗大量的資源
- nonatomic:非線程安全渗勘,適合內(nèi)存小的移動(dòng)設(shè)備
- iOS開(kāi)發(fā)的建議
- 所有屬性都聲明為nonatomic
- 盡量避免多線程搶奪同一塊資源
- 盡量將加鎖矾飞、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理,減小移動(dòng)客戶端的壓力
線程間通信
- 什么叫做線程間通信
- 在1個(gè)進(jìn)程中呀邢,線程往往不是孤立存在的,多個(gè)線程之間需要經(jīng)常進(jìn)行通信
- 線程間通信的體現(xiàn)
- 1個(gè)線程傳遞數(shù)據(jù)給另1個(gè)線程
- 在1個(gè)線程中執(zhí)行完特定任務(wù)后豹绪,轉(zhuǎn)到另1個(gè)線程繼續(xù)執(zhí)行任務(wù)
當(dāng)處理耗時(shí)操作的時(shí)候需要開(kāi)辟一條線程价淌,比如從網(wǎng)絡(luò)上面請(qǐng)求數(shù)據(jù)的時(shí)候申眼,網(wǎng)絡(luò)請(qǐng)求應(yīng)該新開(kāi)辟一條線程,放到子線程里面去蝉衣,然后下載完成之后通知主線程進(jìn)行更新UI括尸。(這樣也是為了線程安全著想)
[self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO];//回到主線程
比如在子線程中進(jìn)行網(wǎng)絡(luò)請(qǐng)求,得到數(shù)據(jù)之后通知主線程更新UI病毡,具體代碼如下
- (void)downImage {
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
// [self performSelectorOnMainThread:@selector(hehe:) withObject:image waitUntilDone:YES];
}
- (void)hehe:(UIImage *)image
{
self.imageView.image = image;
}
小結(jié)
- -(id)init //這個(gè)方法是NSThread的指定初始化方法
- -(id)initWithTarget:(id)target selector:(SEL)seletoc object:(id)argument//初始化一個(gè)線程濒翻,應(yīng)該注意到這個(gè)SEL方法只能有一個(gè)參數(shù),并且選擇的方法不能有返回值啦膜,而且這個(gè)方法只是創(chuàng)建了一個(gè)線程有送,并沒(méi)有開(kāi)始這個(gè)線程,需要調(diào)用start方法開(kāi)始線程
- +(void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument//創(chuàng)建并開(kāi)始一個(gè)線程
- -(void)start//開(kāi)始一個(gè)線程
- +(void)sleepUntilDate:(NSDate *)aDate//阻塞當(dāng)前線程僧家,直到指定時(shí)間
- +(void)sleepForTimeInterval:(NSTimeInterval)time//使線程睡眠一段時(shí)間
- -(BOOL)isExecuting//判斷線程是否正在執(zhí)行
- -(BOOL)isCancelled//判斷線程是否已經(jīng)取消
- -(BOOL)isFinished//判斷線程是否執(zhí)行完
- +(BOOL)isMainThread//判斷當(dāng)前線程是否是主線程
- +(BOOL)isMultiThreaded//判斷是否是多線程的
- +(NSThread *)currentThread//返回當(dāng)前線程
- +(NSThread *)mainThread//返回主線程對(duì)象
- +(NSArray *)callStackReturnAddresses//返回的是這個(gè)線程在棧中所占的地址所組成的數(shù)組
- +(NSArray *)callStackSymbols//返回椚刚空間的符號(hào)
- – threadDictionary //返回線程對(duì)象的字典
- – name //返回線程名字
- – setName: //設(shè)置線程名字
- – stackSize //返回線程所占棧的空間大小
- – setStackSize: //設(shè)置線程所占棧的空間大小
- +threadPriority //返回當(dāng)前線程的優(yōu)先權(quán),其實(shí)返回的是一個(gè)double型數(shù)字八拱,
//從0.0 到1.0 其中1.0最高