1酿炸、同步和異步瘫絮、并發(fā)隊(duì)列和串行隊(duì)列
一般在開發(fā)中,我們使用GCD比較的多填硕,所以就按照GCD來詳細(xì)解釋一下多線程中的同步和異步麦萤、并發(fā)隊(duì)列和串行隊(duì)列鹿鳖。
在上一篇文章中,我們也了解到:
GCD中有2個(gè)用來執(zhí)行任務(wù)的函數(shù):
用同步的方式執(zhí)行任務(wù):
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
用異步的方式執(zhí)行任務(wù):
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);GCD的隊(duì)列可以分為2大類型:
并發(fā)隊(duì)列(Concurrent Dispatch Queue)
可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
串行隊(duì)列(Serial Dispatch Queue)
讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后壮莹,再執(zhí)行下一個(gè)任務(wù))
總結(jié)來說就是:
同步和異步主要影響:能不能開啟新的線程
同步:在當(dāng)前線程中執(zhí)行任務(wù)翅帜,不具備開啟新線程的能力
異步:在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
并發(fā):多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行
串行:一個(gè)任務(wù)執(zhí)行完畢后命满,再執(zhí)行下一個(gè)任務(wù)
2涝滴、實(shí)例解析
// 手動(dòng)生成的串行隊(duì)列,一個(gè)一個(gè)的去執(zhí)行
// 如果是sync(同步的)胶台,不會(huì)開啟新線程歼疮,那就在主線程中執(zhí)行,并且安照順序執(zhí)行dispatch_sync中的任務(wù)概作,再執(zhí)行“完成”
// 如果是async(異步的),會(huì)開啟新線程腋妙,在子線程中執(zhí)行默怨,但是只開啟一條新線程,先執(zhí)行“完成”信息讯榕,再執(zhí)行安照順序執(zhí)行dispatch_async中的任務(wù)
dispatch_queue_t queue = dispatch_queue_create("cn.itcast.serialA", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (int i = 0; i<10; i++) {
NSLog(@"執(zhí)行任務(wù)1 %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i<10; i++) {
NSLog(@"執(zhí)行任務(wù)2 %@",[NSThread currentThread]);
}
});
NSLog(@"完成");
//并發(fā)隊(duì)列,任務(wù)可以一起并行執(zhí)行
//如果是sync(同步的)匙睹,就不會(huì)開啟新的線程愚屁,還是會(huì)在主線程中一個(gè)個(gè)的串行執(zhí)行,先安照順序執(zhí)行dispatch_sync中的任務(wù)痕檬,再執(zhí)行“完成”
//如果是async(異步的)霎槐,會(huì)開啟新的線程,就會(huì)不同的線程中并發(fā)的執(zhí)行任務(wù)梦谜,先執(zhí)行完成丘跌,在并發(fā)執(zhí)行異步任務(wù)
dispatch_queue_t queue = dispatch_queue_create("cn.fichfit.concurrentQueueA", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
for (int i = 0; i<10; i++) {
NSLog(@"執(zhí)行任務(wù)1 %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i=0; i<10; i++) {
NSLog(@"執(zhí)行任務(wù)2 %@",[NSThread currentThread]);
}
});
NSLog(@"完成");
//主隊(duì)列,肯定不會(huì)開啟自線程
//如果是sync(同步的)唁桩,會(huì)報(bào)錯(cuò):因?yàn)樵谥骶€程上,遇到dispatch_sync闭树,會(huì)立即執(zhí)行同步任務(wù),但是執(zhí)行同步任務(wù)之前又要求執(zhí)行“完成”任務(wù)荒澡,互相等待报辱,所以就會(huì)卡住
//如果是async(異步的),會(huì)在主線程中单山,串行執(zhí)行任務(wù)碍现,先執(zhí)行"完成",然后執(zhí)行異步任務(wù)
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (int i = 0; i<10; i++) {
NSLog(@"執(zhí)行任務(wù)1 %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i<10;i++) {
NSLog(@"執(zhí)行任務(wù)2 %@",[NSThread currentThread]);
}
});
NSLog(@"完成");
3、總結(jié)
- <1>看下面的例子米奸,看會(huì)不會(huì)產(chǎn)生死鎖現(xiàn)象昼接?
//sync會(huì)死鎖
//async不會(huì)死鎖
-(void)test1{
NSLog(@"任務(wù)1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2");
});
NSLog(@"任務(wù)3");
}
//block1處會(huì)死鎖
-(void)test2{
NSLog(@"任務(wù)1");
dispatch_queue_t queue = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{//block0
NSLog(@"任務(wù)2");
dispatch_sync(queue, ^{//block1,在執(zhí)行完任務(wù)2后,又向隊(duì)列中加入一個(gè)同步任務(wù)3悴晰,所以會(huì)堵塞住任務(wù)4的執(zhí)行辩棒,因?yàn)闀?huì)產(chǎn)生卡住
NSLog(@"任務(wù)3");
});
NSLog(@"任務(wù)4");
});
NSLog(@"任務(wù)5");
}
//block1處不會(huì)死鎖:如果當(dāng)所處的隊(duì)列不一樣時(shí),不會(huì)產(chǎn)生死鎖
-(void)interview3{
NSLog(@"任務(wù)1");
dispatch_queue_t queue = dispatch_queue_create("myQueue3", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myQueue33", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{//block0
NSLog(@"任務(wù)2");
dispatch_sync(queue2, ^{//block1
NSLog(@"任務(wù)3");
});
NSLog(@"任務(wù)4");
});
NSLog(@"任務(wù)5");
}
總結(jié)一下就是:
使用sync函數(shù)往當(dāng)前串行隊(duì)列中添加任務(wù),會(huì)卡住當(dāng)前的串行隊(duì)列(產(chǎn)生死鎖)
- <2>看下面的例子一睁,觀察打印情況
-(void)test{
NSLog(@"1");
dispatch_queue_t queue = dispatch_queue_create(0, 0);
dispatch_async(queue, ^{
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
});
NSLog(@"3");
}
-(void)run{
NSLog(@"2");
}
打印結(jié)果:(不打印 run中的內(nèi)容)
================================================
1
3
原因是:
主線程的 RunLoop 對(duì)象系統(tǒng)自動(dòng)幫助我們創(chuàng)建好了钻弄,而子線程的 RunLoop對(duì)象需要我們主動(dòng)創(chuàng)建和維護(hù)者吁。
performSelector:withObject:afterDelay:的本質(zhì)是往Runloop中添加定時(shí)器,子線程默認(rèn)沒有啟動(dòng)Runloop复凳,所以不會(huì)執(zhí)行run方法瘤泪。
如下例子則可以保證self.thread處于激活狀態(tài)育八,test方法也會(huì)執(zhí)行。
@interface ViewController ()
@property(nonatomic,strong)NSThread *thread;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"111");
//這里并沒有runloop髓棋,但是NSLog能夠執(zhí)行
//是因?yàn)椴⒉皇侵骶€程所有的事情都交給runloop去做,如是大多數(shù)的事情交給runloop去做按声,比如UI的刷新膳犹、點(diǎn)擊事件的處理签则、performSelector
//如果這里不加上下面的兩句,self.thread就會(huì)失活
//因?yàn)榫€程的任務(wù)一旦執(zhí)行完畢渐裂,生命周期就會(huì)結(jié)束豺旬,無法再使用
//所以說,NSRunLoop就是為了保持使線程保持激活狀態(tài)
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}];
[self.thread start];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//在self.thread線程上執(zhí)行
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}
-(void)test{
NSLog(@"在self.thread線程上執(zhí)行任務(wù)");
}
@end