GCD是什么萌踱?
Grand Central Dispatch或者GCD,是一套低層API槽地,提供了一種新的方法來進(jìn)行并發(fā)程序編寫迁沫。從基本功能上講,GCD有點像NSOperationQueue捌蚊,他們都允許程序?qū)⑷蝿?wù)切分為多個單一任務(wù)然后提交至工作隊列來并發(fā)地或者串行地執(zhí)行集畅。GCD比之NSOpertionQueue更底層更高效,并且它不是Cocoa框架的一部分缅糟。
GCD的原理挺智?
GCD是純C語言的,但它被組建成面向?qū)ο蟮娘L(fēng)格窗宦。GCD對象被稱為dispatch object赦颇。Dispatch object像Cocoa對象一樣是引用計數(shù)的。使用dispatch_release和dispatch_retain函數(shù)來操作dispatch object的引用計數(shù)來進(jìn)行內(nèi)存管理赴涵。
GCD提供很多超越傳統(tǒng)多線程編程的優(yōu)勢:
易用: GCD比之thread跟簡單易用媒怯。由于GCD基于work unit而非像thread那樣基于運算,所以GCD可以控制諸如等待任務(wù)結(jié)束髓窜、監(jiān)視文件描述符扇苞、周期執(zhí)行代碼以及工作掛起等任務(wù)〖淖荩基于block的血統(tǒng)導(dǎo)致它能極為簡單得在不同代碼作用域之間傳遞上下文鳖敷。
效率: GCD被實現(xiàn)得如此輕量和優(yōu)雅,使得它在很多地方比之專門創(chuàng)建消耗資源的線程更實用且快速程拭。這關(guān)系到易用性:導(dǎo)致GCD易用的原因有一部分在于你可以不用擔(dān)心太多的效率問題而僅僅使用它就行了编整。
性能: GCD自動根據(jù)系統(tǒng)負(fù)載來增減線程數(shù)量养筒,這就減少了上下文切換以及增加了計算效率。
GCD的使用
1.GCD的隊列
GCD有三種隊列類型:
1.主隊列:與主線程功能相同叙赚。實際上,提交至 主隊列 的任務(wù)會在主線程中執(zhí)行。 主隊列 可以調(diào)用dispatch_get_main_queue()
來獲得。因為 主隊列 是與主線程相關(guān)的,所以這是一個串行隊列鳍徽。
2.全局隊列:全局隊列是 并發(fā)隊列 ,并由整個進(jìn)程共享敢课。進(jìn)程中存在三個全局隊列:高阶祭、中(默認(rèn))、低三個優(yōu)先級隊列直秆”裟迹可以調(diào)用dispatch_get_global_queue
函數(shù)傳入優(yōu)先級來訪問隊列。
3.自建隊列: 是用函數(shù) dispatch_queue_create
創(chuàng)建的隊列圾结。 可控制創(chuàng)建的隊列是串行還是并行瑰剃。
//DISPATCH_QUEUE_SERIAL 串行
//DISPATCH_QUEUE_CONCURRENT 并行
dispatch_queue_t queue = dispatch_queue_create("LMQueue", DISPATCH_QUEUE_CONCURRENT);
//獲取主隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//獲取全局隊列
/**
第一個參數(shù)
設(shè)置全局隊列的優(yōu)先級
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
QOS_CLASS_USER_INTERACTIVE: 最高優(yōu)先級,交互級別筝野。使用這個優(yōu)先級會占用幾乎所有的系統(tǒng)CUP和I/O帶寬晌姚,僅限用于交互的UI操作,比如處理點擊事件歇竟,繪制圖像到屏幕上挥唠,動畫等
QOS_CLASS_USER_INITIATED: 次高優(yōu)先級,用于執(zhí)行類似初始化等需要立即返回的事件
QOS_CLASS_DEFAULT: 默認(rèn)優(yōu)先級焕议,當(dāng)沒有設(shè)置優(yōu)先級的時候宝磨,線程默認(rèn)優(yōu)先級。一般情況下用的都是這個優(yōu)先級
QOS_CLASS_UTILITY: 普通優(yōu)先級盅安,主要用于不需要立即返回的任務(wù)
QOS_CLASS_BACKGROUND: 后臺優(yōu)先級唤锉,用于用戶幾乎不感知的任務(wù)。
QOS_CLASS_UNSPECIFIED: 未知優(yōu)先級宽堆,表示服務(wù)質(zhì)量信息缺失
第二個參數(shù)
flags: 預(yù)留字段腌紧,傳入任何非0的值都可能導(dǎo)致返回NULL
*/
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2.同步與異步(Synchronous,Asynchronous)
同步茸习,意味著在當(dāng)前線程中執(zhí)行任務(wù)畜隶,不具備開啟新的線程的能力,同步的都是串行執(zhí)行号胚。
異步籽慢,在新的線程中執(zhí)行任務(wù),具備開啟新的線程的能力猫胁。
3.并行和串行(Concurrent,Serial)
任務(wù)串行箱亿,意味著在同一時間,有且只有一個任務(wù)被執(zhí)行弃秆,即一個任務(wù)執(zhí)行完畢之后再執(zhí)行下一個任務(wù)届惋。
任務(wù)并發(fā)髓帽,意味著在同一時間,有多個任務(wù)被執(zhí)行脑豹。
dispatch_queue_t queue = dispatch_queue_create("LMQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1->%@",[NSThread currentThread]);
//同步在當(dāng)前線程執(zhí)行郑藏,如果當(dāng)前線程是主線程,會在主線程運行瘩欺”馗牵或者執(zhí)行線程在主線程的話也會返回主線程。
dispatch_sync(queue, ^{
NSLog(@"4->%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"5->%@",[NSThread currentThread]);
});
dispatch_sync(mainQueue, ^{
NSLog(@"6->%@",[NSThread currentThread]);
});
});
dispatch_async(queue, ^{
NSLog(@"2->%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3->%@",[NSThread currentThread]);
});
1-><NSThread: 0x600000266900>{number = 3, name = (null)} 3-><NSThread: 0x608000263680>{number = 5, name = (null)} 2-><NSThread: 0x618000265cc0>{number = 4, name = (null)} 4-><NSThread: 0x600000266900>{number = 3, name = (null)} 5-><NSThread: 0x600000266900>{number = 3, name = (null)} 6-><NSThread: 0x60000007bc40>{number = 1, name = main}
可以看出如果是同步操作的話俱饿,是在當(dāng)前線程操作不會創(chuàng)建新線程歌粥。異步并行的話,在不同線程中操作拍埠,而且是沒有循序的失驶。
4.死鎖
線程互相等待,即死鎖械拍。
所謂的死鎖是指它們都卡住了突勇,并等待對方完成或執(zhí)行其它操作。第一個不能完成是因為它在等待第二個的完成坷虑。但第二個也不能完成甲馋,因為它在等待第一個的完成。
這個時候就是發(fā)生了死鎖迄损,我們禁止在主隊列(iOS開發(fā)中定躏,主隊列是串行隊列)中,在同步使用主隊列執(zhí)行任務(wù)芹敌,同理痊远,禁止在同一個同步串行隊列中,再使用該串行隊列同步的執(zhí)行任務(wù)氏捞,因為這樣會造成死鎖碧聪。
//1.創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("LMQueue", DISPATCH_QUEUE_SERIAL);
//2.同步執(zhí)行
dispatch_async(queue, ^{
NSLog(@"1");
//再用該隊列執(zhí)行同步任務(wù)會導(dǎo)致死鎖。
//導(dǎo)致死鎖的原因是液茎,隊列是串行要等到前一個任務(wù)完成后才能執(zhí)行下一個逞姿,當(dāng)前串行隊列中再創(chuàng)建一個串行任務(wù),導(dǎo)致隊列一直無法完成任務(wù)出現(xiàn)死鎖現(xiàn)象捆等。
dispatch_sync(queue, ^{
NSLog(@"2");
});
});
4.dispatch_set_target_queue
-(void) testGCD2{
dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(serialQueue, targetQueue);
dispatch_set_target_queue(concurrentQueue, targetQueue);
dispatch_async(serialQueue, ^{
NSLog(@"1>>>%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:3.f];
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2>>>%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:2.f];
});
dispatch_async(concurrentQueue, ^{
NSLog(@"3>>>%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.f];
});
}
1>>><NSThread: 0x618000075900>{number = 3, name = (null)} 2>>><NSThread: 0x618000075900>{number = 3, name = (null)} 3>>><NSThread: 0x618000075900>{number = 3, name = (null)}
調(diào)用dispatch_set_target_queue
會retain
新目標(biāo)隊列queue滞造,release
原有目標(biāo)隊列。設(shè)置目標(biāo)隊列之后栋烤,block將會在目標(biāo)隊列中執(zhí)行谒养。注意:當(dāng)目標(biāo)隊列串行時,任何在目標(biāo)隊列中執(zhí)行的block都會串行執(zhí)行明郭,無論原隊列是否串行
5.dispatch_after
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );
第一個參數(shù)為DISPATCH_TIME_NOW表示當(dāng)前买窟。第二個參數(shù)的delta表示納秒丰泊,一秒對應(yīng)的納秒為1000000000,系統(tǒng)提供了一些宏來簡化
#define NSEC_PER_SEC 1000000000ull //每秒有多少納秒
#define USEC_PER_SEC 1000000ull //每秒有多少毫秒
#define NSEC_PER_USEC 1000ull //每毫秒有多少納秒
這樣如果要表示一秒就可以這樣寫
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);
需要注意的是這里的延時是不精確的始绍,因為加入隊列不一定會立即執(zhí)行趁耗。延時1s可能會1.5s甚至2s之后才會執(zhí)行。
6.dispatch_barrier
dispatch_barrier其阻礙的作用疆虚,在線程任務(wù)沒有完成的時候不允許繼續(xù)執(zhí)行苛败,確保提交的閉包是指定隊列中在特定時段唯一在執(zhí)行的一個,可解決多線程并發(fā)讀寫同一個資源發(fā)生死鎖。
-(void) testBarrier{
dispatch_queue_t queue = dispatch_queue_create("LMBarrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任務(wù)1>>%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"barrier>>%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2>>%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3>>%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4>>%@",[NSThread currentThread]);
});
}
任務(wù)1>><NSThread: 0x60800007d280>{number = 3, name = (null)} barrier>><NSThread: 0x60800007d280>{number = 3, name = (null)} 任務(wù)2>><NSThread: 0x60800007d280>{number = 3, name = (null)} 任務(wù)4>><NSThread: 0x618000262b80>{number = 5, name = (null)} 任務(wù)3>><NSThread: 0x61000007c600>{number = 4, name = (null)}
任務(wù)2径簿,3罢屈,4要在barrier完成后才能執(zhí)行
-(void) testBarrier2{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self setObject:@(i) forKey:[NSString stringWithFormat:@"%d",i]];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i< 5; i++) {
[self objectForKey:[NSString stringWithFormat:@"%d",i]];
}
});
}
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey
{
dispatch_sync(self.queue, ^{
[self.dataDic setObject:anObject forKey:aKey];
NSLog(@"setObject->%@>>>%@",anObject,[NSThread currentThread]);
[NSThread sleepForTimeInterval:1];
});
}
- (id)objectForKey:(id)aKey
{
__block id object = nil;
dispatch_sync(self.queue, ^{
object = [self.dataDic objectForKey:aKey];
NSLog(@"getObject->%@>>>%@",object,[NSThread currentThread]);
});
return object;
}
getObject->0>>><NSThread: 0x60000026afc0>{number = 4, name = (null)} setObject->0>>><NSThread: 0x6080002609c0>{number = 3, name = (null)} getObject->(null)>>><NSThread: 0x60000026afc0>{number = 4, name = (null)} getObject->(null)>>><NSThread: 0x60000026afc0>{number = 4, name = (null)} getObject->(null)>>><NSThread: 0x60000026afc0>{number = 4, name = (null)} getObject->(null)>>><NSThread: 0x60000026afc0>{number = 4, name = (null)} setObject->1>>><NSThread: 0x6080002609c0>{number = 3, name = (null)} setObject->2>>><NSThread: 0x6080002609c0>{number = 3, name = (null)} setObject->3>>><NSThread: 0x6080002609c0>{number = 3, name = (null)} setObject->4>>><NSThread: 0x6080002609c0>{number = 3, name = (null)}
NSMutableDictionary在多個線程中如果同時寫入,或者一個線程寫入一個線程讀取篇亭,會發(fā)生無法預(yù)料的錯誤缠捌。
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey
{
dispatch_barrier_async(self.queue, ^{
[self.dataDic setObject:anObject forKey:aKey];
NSLog(@"setObject->%@>>>%@",anObject,[NSThread currentThread]);
[NSThread sleepForTimeInterval:1];
});
}
我們使用dispatch_barrier_async,讓其單獨執(zhí)行寫入操作译蒂,不允許其他寫入操作或者讀取操作同時執(zhí)行曼月。當(dāng)讀取的時候,我們只需要直接使用dispatch_sync柔昼,讓其正常讀取即可哑芹。這樣就可以保證寫入時不被打擾,讀取時可以多個線程同時進(jìn)行捕透。
setObject->0>>><NSThread: 0x608000077ac0>{number = 3, name = (null)} setObject->1>>><NSThread: 0x608000077ac0>{number = 3, name = (null)} setObject->2>>><NSThread: 0x608000077ac0>{number = 3, name = (null)} setObject->3>>><NSThread: 0x608000077ac0>{number = 3, name = (null)} setObject->4>>><NSThread: 0x608000077ac0>{number = 3, name = (null)} getObject->0>>><NSThread: 0x600000071ec0>{number = 4, name = (null)} getObject->1>>><NSThread: 0x600000071ec0>{number = 4, name = (null)} getObject->2>>><NSThread: 0x600000071ec0>{number = 4, name = (null)} getObject->3>>><NSThread: 0x600000071ec0>{number = 4, name = (null)} getObject->4>>><NSThread: 0x600000071ec0>{number = 4, name = (null)}
7.dispatch_apply
類似for循環(huán)聪姿,但是在并發(fā)隊列的情況下dispatch_apply會并發(fā)執(zhí)行block任務(wù),會等迭代其中的任務(wù)全部完成以后,才會返回。
dispatch_apply可進(jìn)行快速迭代乙嘀,因為可以并行執(zhí)行末购,所以使用dispatch_apply可以運行的更快。
如果在主線程調(diào)用dispatch_apply會阻塞主線程虎谢,可以dispatch_apply放在子線程中執(zhí)行盟榴。
-(void) testApply{
__block int a = 0;
dispatch_apply(9, dispatch_get_global_queue(0, 0), ^(size_t index) {
a += index;
});
NSLog(@"%d",a);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_apply(9, dispatch_get_global_queue(0, 0), ^(size_t index) {
a += index;
});
NSLog(@"%d",a);
});
}
8.set_specific & get_specific
有時候我們需要將某些東西關(guān)聯(lián)到隊列上,比如我們想在某個隊列上存一個東西婴噩,或者我們想?yún)^(qū)分2個隊列擎场。GCD提供了dispatch_queue_set_specific方法,通過key讳推,將context關(guān)聯(lián)到queue上
const void * tag1="tag1";
const void * tag2="tag2";
- (void)viewDidLoad {
[super viewDidLoad];
_queue1 = dispatch_queue_create(tag1, DISPATCH_QUEUE_CONCURRENT);
_queue2 = dispatch_queue_create(tag2, DISPATCH_QUEUE_CONCURRENT);
NSString *string1 = @"string1";
NSString *string2 = @"string2";
//context 將程序中定義的上下文傳遞給參數(shù)顶籽。
dispatch_queue_set_specific(_queue1, tag1, (__bridge void * _Nullable)(string1), NULL);
dispatch_queue_set_specific(_queue2, tag2, (__bridge void * _Nullable)(string2), NULL);
for (int i = 0; i < 5; i++) {
dispatch_async(_queue1, ^{
[self getSpecific];
});
dispatch_async(_queue2, ^{
[self getSpecific];
});
}
}
-(void)getSpecific{
if (dispatch_get_specific(tag1)) {
NSLog(@"tag1->%@",[NSThread currentThread]);
//根據(jù)queue和key取出context玩般,queue參數(shù)不能傳入全局隊列
NSString *context = (__bridge NSString*)(dispatch_queue_get_specific(_queue1, tag1));
NSLog(@"tag1Value->%@",context);
}else if (dispatch_get_specific(tag2)){
NSLog(@"tag2->%@",[NSThread currentThread]);
NSString * context = (__bridge NSString*)(dispatch_queue_get_specific(_queue2, tag2));
NSLog(@"tag2Value->%@",context);
}
}
9.dispatch_set_context和dispatch_set_finalizer_f
context他是將上下文傳遞給參數(shù)银觅,而這個context所需要的內(nèi)容是一個基于C語言的指針,可以通過橋接(__bridge)來把OC的對象轉(zhuǎn)換坏为。
-(void) testContext{
LMContextTest *context = [[LMContextTest alloc] init];
context.name = @"lemon";
dispatch_set_context(_queue1, (__bridge void *)context);
dispatch_async(_queue1, ^{
LMContextTest *context = (__bridge LMContextTest*)dispatch_get_context(_queue1);
NSLog(@"%@",context.name);
});
}
這樣寫會導(dǎo)致context出了作用域就會銷毀究驴,可能導(dǎo)致線程queue1中的context成為野指針而崩潰镊绪。
所以要用到(__bridge_retained
)把context管理權(quán)從ARC中移除,但這樣子context就不能自定釋放洒忧,而我們不知道釋放的時機蝴韭。這時候我們就引入dispatch_set_finalizer_f
方法,在線程銷毀的時候把context也銷毀掉熙侍,dispatch_set_finalizer_f
函數(shù)為dispatch queue設(shè)置清理函數(shù)榄鉴,當(dāng)dispatch queue的引用計數(shù)達(dá)到0時,其所指定的清理函數(shù)就會被調(diào)用蛉抓。
-(void) testContext{
LMContextTest *context = [[LMContextTest alloc] init];
context.name = @"lemon";
//用__bridge_retained增加context的引用計算庆尘,因為context是局部變量出了作用域就會銷毀導(dǎo)致線程中的context成為野指針
dispatch_set_context(_queue1, (__bridge_retained void *)context);
dispatch_set_finalizer_f(_queue1, &cleanContext);
dispatch_async(_queue1, ^{
LMContextTest *data = (__bridge LMContextTest*)dispatch_get_context(_queue1);
NSLog(@"%@",data.name);
});
}
void cleanContext(void *context){
//__bridge_transfer 類型轉(zhuǎn)換Core Foundation->OC對象,同時將管理權(quán)移回ARC
LMContextTest *ct = (__bridge_transfer LMContextTest*)context;
ct = nil;
NSLog(@"clean the data");
}
10.dispatch_once
dispatch_once的作用:對于某個任務(wù)執(zhí)行一次,且只執(zhí)行一次巷送。 dispatch_once函數(shù)有兩個參數(shù)驶忌,第一個參數(shù)predicate用來保證執(zhí)行一次,第二個參數(shù)是要執(zhí)行一次的任務(wù)block笑跛。
+(id) shareMananger {
static dispatch_once_t onceToken;
static LMManager *manager = nil;
dispatch_once(&onceToken, ^{
manager = [[LMManager alloc] init];
});
return manager;
}
使用dispatch_once可以簡化代碼并且徹底保證線程安全付魔,開發(fā)者無需擔(dān)心加鎖或同步。此外飞蹂,dispatch_once更高效几苍,它沒有使用重量級的同步機制,若是那樣做的話陈哑,每次運行代碼前都要獲取鎖擦剑。相反,此函數(shù)采用“原子訪問”來查詢標(biāo)記芥颈,以判斷其所對應(yīng)的代碼原來是否已經(jīng)執(zhí)行過
11.dispatch_groups
dispatch_groups是專門用來監(jiān)視多個異步任務(wù)惠勒。dispatch_group_t實例用來追蹤不同隊列中的不同任務(wù)。
當(dāng)group里所有事件都完成,GCD API有兩種方式發(fā)送通知爬坑,第一種是dispatch_group_wait纠屋,會阻塞當(dāng)前進(jìn)程,等所有任務(wù)都完成或等待超時盾计。第二種方法是使用dispatch_group_notify售担,異步執(zhí)行閉包,不會阻塞署辉。
-(void) testGroup{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("LMQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"%d->%@",i,[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
NSLog(@"222222");
});
dispatch_group_async(group, queue, ^{
NSLog(@"333333");
});
//分組中的任務(wù)完成后會調(diào)用dispatch_group_notify方法
dispatch_group_notify(group, queue, ^{
NSLog(@"任務(wù)全部完成");
});
}
關(guān)于dispatch_group_enter和dispatch_group_leave間的關(guān)系族铆。
dispatch_group_enter
是通知dispatch group任務(wù)開始了。dispatch_group_leave
保持和dispatch_group_enter
配對,通知任務(wù)已經(jīng)完成哭尝。dispatch_group_enter和dispatch_group_leave是成對調(diào)用,如果缺少了dispatch_group_leave哥攘,group不會結(jié)束。
-(void) testGroup3{
dispatch_group_t group = dispatch_group_create();
for(int i = 0; i < 5; i++)
{
dispatch_group_enter(group);
[self block:^{
dispatch_group_leave(group);
}];
}
// dispatch_group_wait等待所有任務(wù)都完成直到超時。如果任務(wù)完成前就超時了逝淹,函數(shù)會返回一個非零值耕姊,可以通過返回值判斷是否超時。也可以用DISPATCH_TIME_FOREVER表示一直等栅葡。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"done");
}
-(void) block:(void(^)())block{
if (block) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"dddd");
block();
});
}
}
12.dispatch_semaphore
另外一種保證同步的方法茉兰。使用dispatch_semaphore_signal加1dispatch_semaphore_wait減1,為0時等待的設(shè)置方式來達(dá)到線程同步的目的和同步鎖一樣能夠解決資源搶占的問題欣簇。
信號量是一個整形值并且具有一個初始計數(shù)值规脸,并且支持兩個操作:信號通知和等待。當(dāng)一個信號量被信號通知熊咽,其計數(shù)會被增加燃辖。當(dāng)一個線程在一個信號量上等待時,線程會被阻塞(如果有必要的話)网棍,直至計數(shù)器大于零黔龟,然后線程會減少這個計數(shù)。
當(dāng)我們在處理一系列線程的時候滥玷,當(dāng)數(shù)量達(dá)到一定量氏身,可以用dispatch_semaphore來控制。
dispatch_semaphore_create
創(chuàng)建一個semaphore
dispatch_semaphore_signal
發(fā)送一個信號,信號量+1
dispatch_semaphore_wait
等待信號惑畴,信號量-1
-(void) testSemaphore{
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, [NSThread currentThread]);
sleep(2);
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"完成");
}
創(chuàng)建了一個初使值為10的semaphore蛋欣,每一次for循環(huán)都會創(chuàng)建一個新的線程,線程結(jié)束的時候會發(fā)送一個信號如贷,線程創(chuàng)建之前會信號等待陷虎,所以當(dāng)同時創(chuàng)建了10個線程之后,for循環(huán)就會阻塞杠袱,等待有線程結(jié)束之后會增加一個信號才繼續(xù)執(zhí)行尚猿,當(dāng)最后的一次dispatch_semaphore_signal
完成后,阻塞結(jié)束楣富。