線程同步(一)

最近突然想了解了解保持線程同步的方式@synchronized、NSLock、dispatch_semaphore、NSCondition

@synchronized() :方式一

NSObject *obj = [[NSObject alloc] init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

@synchronized(obj) {

NSLog(@"需要線程同步的操作1 開始");

NSLog(@"需要線程同步的操作1 結(jié)束");

}

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

@synchronized(obj) {

NSLog(@"需要線程同步的操作2");

}

});

輸出記

?需要線程同步的操作1 開始

需要線程同步的操作1 結(jié)束

需要線程同步的操作2


方式二:

NSObject *obj = [[NSObject alloc] init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

@synchronized(obj) {

NSLog(@"需要線程同步的操作1 開始");

NSLog(@"需要線程同步的操作1 結(jié)束");

}

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

@synchronized(self) {

NSLog(@"需要線程同步的操作2");

}

});

輸出結(jié)果:

需要線程同步的操作2

需要線程同步的操作1 開始

需要線程同步的操作1 結(jié)束

從方式一和二 的輸出不難看出兩者的不同 @synchronized(name) 當那么相同時線程順序輸出,反之,輸入順序不確定



dispatch_semaphore

dispatch_semaphore_t signal = dispatch_semaphore_create(1);

dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

dispatch_semaphore_wait(signal, overTime);

NSLog(@"需要線程同步的操作1 開始");

NSLog(@"需要線程同步的操作1 結(jié)束");

dispatch_semaphore_signal(signal);

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

dispatch_semaphore_wait(signal, overTime);

NSLog(@"需要線程同步的操作2");

dispatch_semaphore_signal(signal);

});

需要線程同步的操作1 開始

需要線程同步的操作1 結(jié)束

需要線程同步的操作2


dispatch_semaphore是GCD用來同步的一種方式,與他相關(guān)的共有三個函數(shù),分別是dispatch_semaphore_create,dispatch_semaphore_signal,dispatch_semaphore_wait。

(1)dispatch_semaphore_create的聲明為:

dispatch_semaphore_t? dispatch_semaphore_create(long value);

傳入的參數(shù)為long,輸出一個dispatch_semaphore_t類型且值為value的信號量。

值得注意的是,這里的傳入的參數(shù)value必須大于或等于0,否則dispatch_semaphore_create會返回NULL。

(2)dispatch_semaphore_signal的聲明為:

long dispatch_semaphore_signal(dispatch_semaphore_t dsema)

這個函數(shù)會使傳入的信號量dsema的值加1;

(3) dispatch_semaphore_wait的聲明為:

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

這個函數(shù)會使傳入的信號量dsema的值減1;這個函數(shù)的作用是這樣的,如果dsema信號量的值大于0,該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語句,并且將信號量的值減1;如果desema的值為0,那么這個函數(shù)就阻塞當前線程等待timeout(注意timeout的類型為dispatch_time_t,不能直接傳入整形或float型數(shù)),如果等待的期間desema的值被dispatch_semaphore_signal函數(shù)加1了,且該函數(shù)(即dispatch_semaphore_wait)所處線程獲得了信號量,那么就繼續(xù)向下執(zhí)行并將信號量減1。如果等待期間沒有獲取到信號量或者信號量的值一直為0,那么等到timeout時,其所處線程自動執(zhí)行其后語句。

dispatch_semaphore 是信號量,但當信號總量設(shè)為 1 時也可以當作鎖來。在沒有等待情況出現(xiàn)時,它的性能比 pthread_mutex 還要高,但一旦有等待情況出現(xiàn)時,性能就會下降許多。相對于 OSSpinLock 來說,它的優(yōu)勢在于等待時不會消耗 CPU 資源。

NSLock

NSLock *lock = [[NSLock alloc] init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

//[lock lock];

[lock lockBeforeDate:[NSDate date]];

NSLog(@"需要線程同步的操作1 開始");

NSLog(@"需要線程同步的操作1 結(jié)束");

[lock unlock];

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

if ([lock tryLock]) {//嘗試獲取鎖,如果獲取不到返回NO,不會阻塞該線程

NSLog(@"鎖可用的操作");

[lock unlock];

}else{

NSLog(@"鎖不可用的操作");

}

NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:3];

if ([lock lockBeforeDate:date]) {//嘗試在未來的3s內(nèi)獲取鎖,并阻塞該線程,如果3s內(nèi)獲取不到恢復線程, 返回NO,不會阻塞該線程

NSLog(@"沒有超時,獲得鎖");

[lock unlock];

}else{

NSLog(@"超時,沒有獲得鎖");

}

});

NSLock是Cocoa提供給我們最基本的鎖對象,這也是我們經(jīng)常所使用的,除lock和unlock方法外,NSLock還提供了tryLock和lockBeforeDate:兩個方法,前一個方法會嘗試加鎖,如果鎖不可用(已經(jīng)被鎖住),剛并不會阻塞線程,并返回NO。lockBeforeDate:方法會在所指定Date之前嘗試加鎖,如果在指定時間之前都不能加鎖,則返回NO。



NSRecursiveLock遞歸鎖


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);

RecursiveMethod(value - 1);

}

[lock unlock];

};

RecursiveMethod(5);

});

NSRecursiveLock實際上定義的是一個遞歸鎖,這個鎖可以被同一線程多次請求,而不會引起死鎖。這主要是用在循環(huán)或遞歸操作中。


NSConditionLock


NSConditionLock * lock = [NSConditionLock new];

NSMutableArray *products = [NSMutableArray array];

NSInteger HAS_DATA = 1;

NSInteger NO_DATA = 0;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

while (1) {

[lock lockWhenCondition:NO_DATA];

[products addObject:[[NSObject alloc] init]];

NSLog(@"produce a product,總量:%zi",products.count);

[lock unlockWithCondition:HAS_DATA];

}

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

while (1) {

NSLog(@"wait for product");

[lock lockWhenCondition:HAS_DATA];

[products removeObjectAtIndex:0];

NSLog(@"custome a product");

[lock unlockWithCondition:NO_DATA];

}

});

NSCondition

NSCondition *condition = [[NSCondition alloc] init];

NSMutableArray *products = [NSMutableArray array];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

while (1) {

[condition lock];

if ([products count] == 0) {

NSLog(@"wait for product");

[condition wait];

}

[products removeObjectAtIndex:0];

NSLog(@"custome a product");

[condition unlock];

}

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

while (1) {

[condition lock];

[products addObject:[[NSObject alloc] init]];

NSLog(@"produce a product,總量:%zi",products.count);

[condition signal];

[condition unlock];

}

});

[condition lock];一般用于多線程同時訪問、修改同一個數(shù)據(jù)源,保證在同一時間內(nèi)數(shù)據(jù)源只被訪問、修改一次,其他線程的命令需要在lock 外等待,只到unlock ,才可訪問

[condition unlock];與lock 同時使用

[condition wait];讓當前線程處于等待狀態(tài)

[condition signal];CPU發(fā)信號告訴線程不用在等待,可以繼續(xù)執(zhí)行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容