+++
Categories = ["iOS",]
Tags = ["iOS","Lock",]
date = "2015-08-27T17:35:29+08:00"
title = "iOS 多線程鎖"
+++
dispatch_semaphore
信號(hào)量是一個(gè)整形值并且具有一個(gè)初始計(jì)數(shù)值,并且支持兩個(gè)操作:信號(hào)通知和等待。當(dāng)一個(gè)信號(hào)量被信號(hào)通知,其計(jì)數(shù)會(huì)被增加。當(dāng)一個(gè)線程在一個(gè)信號(hào)量上等待時(shí),線程會(huì)被阻塞(如果有必要的話),直至計(jì)數(shù)器大于零,然后線程會(huì)減少這個(gè)計(jì)數(shù)。
在GCD中有三個(gè)函數(shù)是semaphore的操作,分別是:
- dispatch_semaphore_create 創(chuàng)建一個(gè)semaphore
- dispatch_semaphore_signal 發(fā)送一個(gè)信號(hào)
- dispatch_semaphore_wait 等待信號(hào)
簡(jiǎn)單的介紹一下這三個(gè)函數(shù),第一個(gè)函數(shù)有一個(gè)整形的參數(shù),我們可以理解為信號(hào)的總量,dispatch_semaphore_signal是發(fā)送一個(gè)信號(hào),自然會(huì)讓信號(hào)總量加1,dispatch_semaphore_wait等待信號(hào),當(dāng)信號(hào)總量少于0的時(shí)候就會(huì)一直等待,否則就可以正常的執(zhí)行,并讓信號(hào)總量-1,根據(jù)這樣的原理,我們便可以快速的創(chuàng)建一個(gè)并發(fā)控制來(lái)同步任務(wù)和有限資源訪問控制。
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
NSLog(@"%i",i);
sleep(2);
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
dispatch_release(semaphore);
上面代碼創(chuàng)建了一個(gè)初使值為10的semaphore,每一次for循環(huán)都會(huì)創(chuàng)建一個(gè)新的線程,線程結(jié)束的時(shí)候會(huì)發(fā)送一個(gè)信號(hào),線程創(chuàng)建之前會(huì)信號(hào)等待,所以當(dāng)同時(shí)創(chuàng)建了10個(gè)線程之后,for循環(huán)就會(huì)阻塞,等待有線程結(jié)束之后會(huì)增加一個(gè)信號(hào)才繼續(xù)執(zhí)行,如此就形成了對(duì)并發(fā)的控制,如上就是一個(gè)并發(fā)數(shù)為10的一個(gè)線程隊(duì)列。
pthread_mutex_t
如果互斥鎖類型為 PTHREAD_MUTEX_NORMAL,則不提供死鎖檢測(cè)。嘗試重新鎖定互斥鎖會(huì)導(dǎo)致死鎖。如果某個(gè)線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或未鎖定,則將產(chǎn)生不確定的行為。
如果互斥鎖類型為 PTHREAD_MUTEX_ERRORCHECK,則會(huì)提供錯(cuò)誤檢查。如果某個(gè)線程嘗試重新鎖定的互斥鎖已經(jīng)由該線程鎖定,則將返回錯(cuò)誤。如果某個(gè)線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或者未鎖定,則將返回錯(cuò)誤。
如果互斥鎖類型為 PTHREAD_MUTEX_RECURSIVE,則該互斥鎖會(huì)保留鎖定計(jì)數(shù)這一概念。線程首次成功獲取互斥鎖時(shí),鎖定計(jì)數(shù)會(huì)設(shè)置為 1。線程每重新鎖定該互斥鎖一次,鎖定計(jì)數(shù)就增加 1。線程每解除鎖定該互斥鎖一次,鎖定計(jì)數(shù)就減小 1。 鎖定計(jì)數(shù)達(dá)到 0 時(shí),該互斥鎖即可供其他線程獲取。如果某個(gè)線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或者未鎖定,則將返回錯(cuò)誤。
如果互斥鎖類型是 PTHREAD_MUTEX_DEFAULT,則嘗試以遞歸方式鎖定該互斥鎖將產(chǎn)生不確定的行為。對(duì)于不是由調(diào)用線程鎖定的互斥鎖,如果嘗試解除對(duì)它的鎖定,則會(huì)產(chǎn)生不確定的行為。如果嘗試解除鎖定尚未鎖定的互斥鎖,則會(huì)產(chǎn)生不確定的行為。
//主線程中
TestObj *obj = [[TestObj alloc] init];
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex);
[obj method1];
sleep(5);
pthread_mutex_unlock(&mutex);
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
pthread_mutex_lock(&mutex);
[obj method2];
pthread_mutex_unlock(&mutex);
});
@synchronized
//主線程中
TestObj *obj = [[TestObj alloc] init];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(obj){
[obj method1];
sleep(10);
}
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
@synchronized(obj){
[obj method2];
}
});
NSLock
//主線程中
TestObj *obj = [[TestObj alloc] init];
NSLock *lock = [[NSLock alloc] init];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
[obj method1];
sleep(10);
[lock unlock];
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保證讓線程2的代碼后執(zhí)行
[lock lock];
[obj method2];
[lock unlock];
});
NSLock是Cocoa提供給我們最基本的鎖對(duì)象,這也是我們經(jīng)常所使用的,除lock和unlock方法外,NSLock還提供了tryLock和lockBeforeDate:兩個(gè)方法,前一個(gè)方法會(huì)嘗試加鎖,如果鎖不可用(已經(jīng)被鎖住),剛并不會(huì)阻塞線程,并返回NO。lockBeforeDate:方法會(huì)在所指定Date之前嘗試加鎖,如果在指定時(shí)間之前都不能加鎖,則返回NO。
NSRecursiveLock
NSRecursiveLock實(shí)際上定義的是一個(gè)遞歸鎖,這個(gè)鎖可以被同一線程多次請(qǐng)求,而不會(huì)引起死鎖。這主要是用在循環(huán)或遞歸操作中。我們先來(lái)看一個(gè)示例:
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value = %d", value);
sleep(2);
RecursiveMethod(value - 1);
}
[lock unlock];
};
RecursiveMethod(5);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
BOOL flag = [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
if (flag) {
NSLog(@"lock before date");
[lock unlock];
} else {
NSLog(@"fail to lock before date");
}
});
NSRecursiveLock除了實(shí)現(xiàn)NSLocking協(xié)議的方法外,還提供了兩個(gè)方法,分別如下:
// 在給定的時(shí)間之前去嘗試請(qǐng)求一個(gè)鎖
- (BOOL)lockBeforeDate:(NSDate *)limit
// 嘗試去請(qǐng)求一個(gè)鎖,并會(huì)立即返回一個(gè)布爾值,表示嘗試是否成功
- (BOOL)tryLock
另外,NSRecursiveLock還聲明了一個(gè)name屬性,如下:
@property(copy) NSString *name
我們可以使用這個(gè)字符串來(lái)標(biāo)識(shí)一個(gè)鎖。Cocoa也會(huì)使用這個(gè)name作為錯(cuò)誤描述信息的一部分。
NSCondition
使用NSCondition,實(shí)現(xiàn)多線程的同步,即,可實(shí)現(xiàn)生產(chǎn)者消費(fèi)者問題。
基本思路是,首先要?jiǎng)?chuàng)建公用的NSCondition實(shí)例。然后:
消費(fèi)者取得鎖,取產(chǎn)品,如果沒有,則wait,這時(shí)會(huì)釋放鎖,直到有線程喚醒它去消費(fèi)產(chǎn)品;
生產(chǎn)者制造產(chǎn)品,首先也是要取得鎖,然后生產(chǎn),再發(fā)signal,這樣可喚醒wait的消費(fèi)者。
- (IBAction)conditionTest:(id)sender
{
NSLog(@"begin condition works!");
products = [[NSMutableArray alloc] init];
condition = [[NSCondition alloc] init];
[NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil];
}
- (void)createConsumenr
{
[condition lock];
while ([products count] == 0) {
NSLog(@"wait for products");
[condition wait];
}
[products removeObjectAtIndex:0];
NSLog(@"comsume a product");
[condition unlock];
}
- (void)createProducter
{
[condition lock];
[products addObject:[[NSObject alloc] init]];
NSLog(@"produce a product");
[condition signal];
[condition unlock];
}
NSConditionLock
//主線程中
NSConditionLock *theLock = [[NSConditionLock alloc] init];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i=0;i<=2;i++)
{
[theLock lock];
NSLog(@"thread1:%d",i);
sleep(2);
[theLock unlockWithCondition:i];
}
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[theLock lockWhenCondition:2];
NSLog(@"thread2");
[theLock unlock];
});