所以這里同步應(yīng)該不是一起、而是共同完成的意思,可理解為協(xié)調(diào)就是按預(yù)定的先后次序進行工作,好比:不要和我搶了,你先等會我做完了你在做
線程同步目的為了多個線程都能很好的工作,合理的訪問系統(tǒng)資源不爭不搶、和諧共處。iOS開發(fā)中常用的保持線程同步有以下幾種:
- 通過線程加鎖
- 串行隊列
- GCD
線程加鎖
常用的幾種形式的鎖
- 1、 @synchronized
- (void)myMethod:(id)anObj
{
@synchronized(anObj)
{
//執(zhí)行的代碼操作
}
}
通過synchronized指令自動的添加一個互斥鎖,底層通過pthread_mutex實現(xiàn)。通過對一段代碼的使用進行加鎖。其他試圖執(zhí)行該段代碼的線程都會被阻塞,直到加鎖線程退出執(zhí)行該段被保護的代碼段。
當(dāng)在@synchronized()代碼塊中拋出異常的時候,Objective-C運行時會捕獲到該異常,并釋放信號量,并把該異常重新拋出給下一個異常處理者。
一個線程是可以以遞歸的方式多次調(diào)用myMethod。
關(guān)于參數(shù)anObj;
作為一個唯一標識符來標記當(dāng)前線程加鎖操作必須是個對象類型,所以對于同一個操作不同的線程應(yīng)該用同一個對象,否則無法起到標記加鎖的作用。 不能為空nil。
常見的基本都是self
@synchronized(self)
{
//執(zhí)行的代碼操作
}
self作為標記符十分常見,但是很明顯會有一個問題:
//方法1
- (void)myMethod1:(id)anObj
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0), ^{
@synchronized(anObj)
{
//執(zhí)行的代碼操作
}
});
}
//方法2
- (void)myMethod2:(id)anObj
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0), ^{
@synchronized(anObj)
{
//執(zhí)行的代碼操作
}
});
}
myMethod1(self);
myMethod2(self)
如果myMethod1、myMethod2沒用任何關(guān)系,如果此時執(zhí)行myMethod1,那么myMethod2就只能等待其執(zhí)行完成。所以這種情況更細的粒度來加鎖,使用各自的對象互不影響更為合理。
- 2、NSLock
NSLock * lock = [[NSLock alloc]init];
[lock lock];
//執(zhí)行的代碼操作
[lock unlock];
底層通過pthread_mutex實現(xiàn);方法lock、unlock必須成對出現(xiàn),必須在同一個線程中操作否則無效。不支持遞歸,如果多次調(diào)用會造成死鎖。
如果多個線程共用一個lock ,一個線程加鎖后其他請求加鎖的線程會形成一個等待隊列、按照先進先出的規(guī)則等待鎖釋放后再加鎖(待驗證)。
3、NSRecursiveLock 遞歸鎖類似NSLock,但它可以在同一個線程中反復(fù)加鎖且不會造成死鎖。
4、 NSCondition 基于信號量方式實現(xiàn)的鎖對象,提供單獨的信號量管理接口。底層通過
pthread_cond_t實現(xiàn)。
NSCondition對象包含鎖和條件檢測功能,類似于生產(chǎn)者和消費者:消費者消費資源如果沒有就繼續(xù)等待,生產(chǎn)者提供資源然后發(fā)出信號激活消費者。鎖的作用就是用來保護這一操作防止被其他線程干擾。不管你是大牛還是小白都歡迎入駐
isWait = true;
condition = [[NSCondition alloc]init];
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf _user];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf _produce];
});
-(void)_user{
[condition lock];
while (isWait) {
//等待其他線程發(fā)出信號,[condition signal];
//阻塞當(dāng)前線程
NSLog(@"等待條件滿足");
[condition wait];
}
{
//執(zhí)行操作
NSLog(@"執(zhí)行操作");
}
//完成
[condition unlock];
NSLog(@"完成");
}
-(void)_produce{
[condition lock];
isWait = false;
[condition signal];
[condition unlock];
}
輸出結(jié)果:
[13781:212898] 等待條件滿足
[13781:212898] 執(zhí)行操作
[13781:212898] 完成
5、 NSConditionLock 可以使用特定值來加鎖和解鎖,和
NSCondition表現(xiàn)差不多。(instancetype)initWithCondition:(NSInteger)condition參數(shù)condition作為標識符更容易理解,lockWhenCondition獲取指定標記的鎖沒有的話就阻塞當(dāng)前線程,unlockWithCondition:釋放指定標記的鎖,等他的線程獲取鎖然后繼續(xù)執(zhí)行操作。
使用上比NSCondition更方便些,代碼更簡潔。
用NSConditionLock改寫以上代碼:
-(void)_testConditionLock{
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf _user1];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf _produce1];
});
}
-(void)_user1{
NSLog(@"等待條件滿足");
[conditionLock lockWhenCondition:11];
NSLog(@"條件滿足了");
{
//執(zhí)行操作
NSLog(@"執(zhí)行操作");
}
//完成
[conditionLock unlockWithCondition:0];
NSLog(@"完成");
}
[self _testConditionLock];
輸出結(jié)果:
[7812:120141] 等待條件滿足
[7812:120137] 生成條件中...
[7812:120141] 條件滿足了
[7812:120141] 執(zhí)行操作
[7812:120141] 完成
- 6、 其他不常用的鎖
pthread_mutex pthread_mutex(recursive)POSIX標準的unix多線程,C 語言下多線程實現(xiàn)。
OSSpinLock:自旋鎖,一直輪詢等待時會消耗大量 CPU 資源。
串行隊列
通過創(chuàng)建一個串行隊列,把我們的操作添加到隊列。
dispatch_queue_t queue = dispatch_queue_create("com.queue.test",DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"task 1");
});
dispatch_async(queue, ^{
NSLog(@"task 2");
});
dispatch_async(queue, ^{
NSLog(@"task 3");
});
感覺創(chuàng)建隊列、添加操作到隊列太麻煩,不夠簡潔而且隊列的調(diào)度肯定占用不少資源.
GCD
通過dispatch_semaphore信號量實現(xiàn)線程同步
dispatch_semaphore_create(long value);
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);//-1
dispatch_semaphore_signal(dispatch_semaphore_t dsema);//+1
dispatch_semaphore_wait在信號量為0時會阻塞當(dāng)前線程,等待dispatch_semaphore_signal釋放信號然后繼續(xù)執(zhí)行。
用信號量改寫以上代碼:
-(void)_testSemaphore{
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf _user2];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf _produce2];
});
}
-(void)_user2{
NSLog(@"等待條件滿足");
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
NSLog(@"條件滿足了");
{
//執(zhí)行操作
NSLog(@"執(zhí)行操作");
}
//完成
NSLog(@"完成");
}
-(void)_produce2{
NSLog(@"生成條件中...");
dispatch_semaphore_signal(semaphore);
}
semaphore = dispatch_semaphore_create(0);
NSLog(@"初始化信號0");
[self _testSemaphore];
多線程并發(fā)的四種方式
ios多線程開發(fā)的常用四種方式 (附有demo)
- pthread
- NSThread
- NSOperation\NSOperationQueue
- GCD
一 、pthread
C語言通用的多線程API,跨平臺,程序員手動管理線程生命周期,使用難度大
//創(chuàng)建線程
NSLog(@"開始執(zhí)行");
int pthread_create(pthread_t * __restrict ,const pthread_attr_t * __restrict ,void *(*)(void *),void * __restrict);
//使用
pthread_t pthread;
pthread_create(&pthread, NULL, function, NULL);
NSLog(@"執(zhí)行結(jié)束");
void * function(void * param) {
for (int i = 0; i < 5; i ++) {
NSLog(@"i : %d-- 線程: %@",i,[NSThread currentThread]);
}
return NULL;
}
二、 NSThread
創(chuàng)建NSThread 有三種方法
//公共方法
- (void)threadMoth {
NSLog(@"%@",[NSThread currentThread]);
}
// 方法 1 需要手動開啟
NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
[thread start];
//方法 2
[NSThread detachNewThreadSelector:@selector(threadMoth) toTarget:self withObject:nil];
//方法 3
[self performSelectorInBackground:@selector(threadMoth) withObject:nil];
//常用的相關(guān)方法
[NSThread mainThread];// 獲得主線程
[NSThread isMainThread]; //是否為主線程
NSLog(@"shuchu--%@--%d",[NSThread mainThread],[NSThread isMainThread]);
NSLog(@"休眠前");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4.0]];//阻塞線程 以當(dāng)前時間為準阻塞4秒
NSLog(@"休眠1");
[NSThread sleepForTimeInterval:2];//阻塞線程 2 秒
NSLog(@"休眠后");
[NSThread exit];// 強制退出線程
方法2、一調(diào)用就會立即創(chuàng)建一個線程來做事情;
方法1、需要手動調(diào)用 start 啟動線程時才會真正去創(chuàng)建線程。
方法3、是利用NSObject的方法 performSelectorInBackground:withObject: 來創(chuàng)建一個線程:在后臺運行某一個方法
與 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一樣的。
運行結(jié)果
三、NSOperation、NSOperationQueue
NSOperation、NSOperationQueue 是蘋果提供給我們的一套多線程解決方案。實際上 NSOperation、NSOperationQueue 是基于 GCD 更高一層的封裝,完全面向?qū)ο?。但是?GCD 更簡單易用、代碼可讀性也更高。
(1)、NSOperation
NSOperation 是個抽象類,不能用來封裝操作。我們只有使用它的子類來封裝操作。我們有兩種方式來封裝操作。
- 子類 NSInvocationOperation
- 子類 NSBlockOperation
1、 子類 NSInvocationOperation
- 創(chuàng)建 NSInvocationOperation 對象
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget
- 調(diào)用 start 方法開始執(zhí)行操作
[operation start];