前提簡(jiǎn)述:
常用的線程方案有Pthread色乾,NSThread, GCD,NSOperation。以下是比較:
pthread : 是一套通用的C語(yǔ)言多線程API,適用于Unix \ Linux\ Windows等系統(tǒng),可跨平臺(tái)\可移植性号显,學(xué)習(xí)難度大。線程生命周期需要程序員管理躺酒。
NSThread: 是OC語(yǔ)言押蚤,使用更加面向?qū)ο螅苯硬僮骶€程對(duì)象羹应,程序員需要管理生命周期揽碘。
GCD: 可替代NSThread等線程技術(shù),C語(yǔ)言實(shí)現(xiàn)园匹,系統(tǒng)自動(dòng)管理钾菊。
NSOperation: 基于GCD的封裝成OC對(duì)象,比GCD多了一些更簡(jiǎn)單實(shí)用的功能偎肃,使用更加面向?qū)ο螅到y(tǒng)自動(dòng)管理線程生命周期
另附上兩個(gè)案例:
?dispatch_async(dispatch_get_global_queue(0, 0), ^{
? ? ? ? NSLog(@"1");
? ? ? ? [self performSelector:@selector(test) withObject:nil afterDelay:0];
? ? ? ? NSLog(@"3");
? ? })
-(void)test { NSLog(@"2") ;}
打印結(jié)果是:1浑此、3累颂。
原因:performSelector…afterDelay是向runloop中添加了一個(gè)timer定時(shí)器,子線程中默認(rèn)是沒有啟動(dòng)runloop的凛俱,所以不會(huì)執(zhí)行紊馏。
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event {
? ? NSThread*thread = [[NSThreadalloc]initWithBlock:^{
? ? ? ? NSLog(@"1");
? ? }];
? ? [threadstart];
? ? BOOLisOk =true;
? ? // isOk 設(shè)置為YES時(shí),會(huì)導(dǎo)致crash蒲犬, 因?yàn)樵谧泳€程runloop未開啟朱监,block執(zhí)行完畢后,Threadj即退出原叮;會(huì)提示此信息:performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform'
? ? // 設(shè)置為NO赫编,test不執(zhí)行。
? ? [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:isOk];
}
多線程存在安全隱患奋隶,同一個(gè)數(shù)據(jù)可能會(huì)被多個(gè)線程共享擂送,也就是多個(gè)線程可能會(huì)訪問同一塊資源,當(dāng)多個(gè)線程訪問同一塊資源時(shí)唯欣,很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問題嘹吨。這種情況,我們需要使用線程同步技術(shù)(同步境氢,就是協(xié)同步調(diào)蟀拷,按預(yù)定的先后次序進(jìn)行)解決碰纬。常用的方式是加鎖。
iOS線程同步方案:
1,?OSSpinLock 自旋鎖:
OSSpinLock叫做”自旋鎖”问芬,等待鎖的線程會(huì)處于忙等(busy-wait)狀態(tài)悦析,一直占用著CPU資源
目前已經(jīng)不再安全,可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問題
如果等待鎖的線程優(yōu)先級(jí)較高愈诚,它會(huì)一直占用著CPU資源她按,優(yōu)先級(jí)低的線程就無法釋放鎖
需要導(dǎo)入頭文件#import<libkern/OSAtomic.h>
OSSpinLock lock =?OS_SPINLOCK_INIT;
OSSpinLockLock(&_moneyLock);
//coding here
?OSSpinLockUnlock(&_moneyLock);
2,?os_unfair_lock
nos_unfair_lock用于取代不安全的OSSpinLock ,從iOS10開始才支持
n從底層調(diào)用看炕柔,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài)酌泰,并非忙等
n需要導(dǎo)入頭文件#import<os/lock.h>
os_unfair_lock lock =?OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&_ticketLock);
// coding here
os_unfair_lock_unlock(&_ticketLock);
3,?pthread_mutex
mutex叫做”互斥鎖”,等待鎖的線程會(huì)處于休眠狀態(tài)匕累。需要導(dǎo)入頭文件#import<pthread.h>
{ ? ?// 靜態(tài)初始化
? ? pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
? ??// 初始化屬性
? ? pthread_mutexattr_t attr;
? ? pthread_mutexattr_init(&attr);
? ? pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// ? ?初始化鎖
? ? pthread_mutex_init(mutex, &attr);
// ? 銷毀屬性
? ? pthread_mutexattr_destroy(&attr);
? ? // 初始化鎖 ?NULL表示默認(rèn)屬性
? ? pthread_mutex_init(mutex, NULL);
}
{
?????pthread_mutex_lock(&_ticketMutex);
????//coding here...
????pthread_mutex_unlock(&_ticketMutex);
}
- (void)dealloc{
? ? pthread_mutex_destroy(&mutex);
}
4, pthread_mutex 遞歸鎖
用法和3類似瓤檐,上述pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)
5, pthread_mutex – 條件鎖
使用方法展示:
?pthread_mutex_t mutex;
?pthread_mutex_init(&mutex, NULL); //NULL表示使用默認(rèn)屬性
? // 初始化條件
?pthread_cond_t condition;
?pthread_cond_init(&condition, NULL);
// 等待條件(進(jìn)入休眠,釋放mutex鎖惫东; 被喚醒后揩局,會(huì)再次對(duì)mutex加鎖)
pthread_cond_wait(&condition, &mutex);
//激活一個(gè)等待該條件的線程
pthread_cond_signal(&condition);
//激活所有等待該條件的線程
pthread_cond_broadcast(&condition);
- (void) del{
????pthread_mutex_lock(&_mutex);
? ? pthread_cond_wait(&_cond, &_mutex);
????// coding here
? ? pthread_mutex_unlock(&_mutex);
}
-(void)append {
????pthread_mutex_lock(&_mutex);
????// coding here
? ? pthread_cond_signal(&_cond);??// 信號(hào)
? ? pthread_mutex_unlock(&_mutex);
}
- (void)dealloc {
? ? pthread_mutex_destory(&mutex);
? ? pthread_cond_destory(&condition);
}
6,NSLock
NSLock是對(duì)mutex普通鎖的封裝, 遵守NSLocking協(xié)議
7,NSRecursiveLock
NSRecursiveLock也是對(duì)mutex遞歸鎖的封裝,API跟NSLock基本一致
8炼蹦,NSCondition
NSCondition是對(duì)mutex和cond的封裝羡宙,使用演示如下:
NSCondition *condition = [[NSCondition alloc] init];
- (void)del{
? ? [condition lock];
? ? [condition wait];??// 等待
? ? // coding here
? ? [condition unlock];
}
- (void)add{
? ? [self.condition lock];
????// coding here
? ??[self.condition broadcast];
? ? [self.condition unlock];
}
9,?NSConditionLock
NSConditionLock是對(duì)NSCondition的進(jìn)一步封裝,可以設(shè)置具體的條件值掐隐。簡(jiǎn)列使用如下:
int start = 0; 設(shè)置某個(gè)啟動(dòng)條件
NSConditionLock *conditionLock =?[[NSConditionLock alloc] initWithCondition: start];
[conditionLock lock];
[conditionLock lockWhenCondition: some];
[conditionLock unlockWithCondition:some];
[conditionLock unlock];
10狗热,dispatch_semaphore
semaphore叫做”信號(hào)量”,信號(hào)量的初始值虑省,可以用來控制線程并發(fā)訪問的最大數(shù)量匿刮,信號(hào)量的初始值為1,代表同時(shí)只允許1條線程訪問資源探颈,可以用來保證線程同步熟丸。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(x);
//當(dāng)信號(hào)量的值<=0,當(dāng)前線程就會(huì)進(jìn)入休眠等待(直到信號(hào)量的值大于0)
//如果信號(hào)量的值>0,就減1,然后執(zhí)行后面的代碼
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(semaphore);
11伪节,?dispatch_queue
直接使用GCD的串行隊(duì)列光羞,也是可以實(shí)現(xiàn)線程同步的。
12架馋,@synchronized
@synchronized是對(duì)mutex遞歸鎖的封裝狞山;@synchronized(obj)內(nèi)部會(huì)生成obj對(duì)應(yīng)的遞歸鎖,然后進(jìn)行加鎖叉寂、解鎖操作萍启。
@synchronized (token) {}
13,?atomic用于保證屬性setter、getter的原子性操作,相當(dāng)于在getter和setter內(nèi)部加了線程同步的鎖,?它并不能保證使用屬性的過程是線程安全的勘纯。
線程同步方案性能比較,性能從高到低排序依次是:
os_unfair_lock >?OSSpinLock >?dispatch_semaphore >?pthread_mutex >?pdispatch_queue(DISPATCH_QUEUE_SERIAL) > ?NSLock >?NSCondition >?ppthread_mutex(recursive) >?NSRecursiveLock >?NSConditionLock >?@synchronized
自旋鎖局服、互斥鎖比較:
a, 什么情況使用自旋鎖比較劃算?
預(yù)計(jì)線程等待鎖的時(shí)間很短
加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用驳遵,但競(jìng)爭(zhēng)情況很少發(fā)生
CPU資源不緊張
多核處理器
b,什么情況使用互斥鎖比較劃算淫奔?
預(yù)計(jì)線程等待鎖的時(shí)間較長(zhǎng)
單核處理器
臨界區(qū)有IO操作
臨界區(qū)代碼復(fù)雜或者循環(huán)量大
臨界區(qū)競(jìng)爭(zhēng)非常激烈