我們平時(shí)在項(xiàng)目開(kāi)發(fā)過(guò)程中經(jīng)常會(huì)用到多線程相關(guān)的技術(shù)侨赡,經(jīng)常會(huì)使用多線程來(lái)進(jìn)行網(wǎng)絡(luò)請(qǐng)求和數(shù)據(jù)傳輸?shù)炔僮骼甙危琌C中創(chuàng)建多線程主要有以下幾種方式:
- pthread
- NSThread
- GCD
- NSOperation
這四種創(chuàng)建多線程區(qū)別對(duì)比如圖:
我們?cè)谄綍r(shí)的項(xiàng)目開(kāi)發(fā)過(guò)程中棍掐,使用最多的創(chuàng)建多線程的方式就是GCD
贴铜,接下來(lái)我們看下GCD
的常見(jiàn)用法
GCD
有兩種執(zhí)行任務(wù)的方式:
- dispatch_snyc(同步執(zhí)行任務(wù)):任務(wù)在當(dāng)前主線程中執(zhí)行腊敲,并沒(méi)有開(kāi)啟新線程(dispatch_snyc不具備開(kāi)啟異步線程的能力)
- dispatch_async(異步執(zhí)行任務(wù)):任務(wù)在子線程中執(zhí)行恩闻,dispatch_async會(huì)開(kāi)啟一個(gè)異步子線程(dispatch_async具備開(kāi)啟新線程的能力)
GCD
隊(duì)列(queue)也分為兩種類型:
- 串行隊(duì)列(Serial Dispatch Queue):多個(gè)任務(wù)一個(gè)挨著一個(gè)的有序執(zhí)行艺糜,上一個(gè)任務(wù)執(zhí)行完接著執(zhí)行下一個(gè)任務(wù)
- 并發(fā)隊(duì)列(Concurrent Dispatch Queue):多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行,自動(dòng)開(kāi)啟多個(gè)線程來(lái)同時(shí)執(zhí)行任務(wù)
上面我們提到了兩個(gè)概念
-
任務(wù):可以理解為是多線程需要做的事情幢尚,在
GCD
中任務(wù)就是對(duì)應(yīng)的block
代碼塊內(nèi)需要執(zhí)行的代碼破停,任務(wù)又分為同步
和異步
,同步
和異步
的主要區(qū)別在于是否具備開(kāi)啟新線程的能力- 同步:任務(wù)在當(dāng)前主線程中執(zhí)行尉剩,不具備開(kāi)啟新線程的能力
- 異步:任務(wù)在新開(kāi)啟的子線程中執(zhí)行真慢,具備開(kāi)啟新線程的能力
-
隊(duì)列:可以理解為是控制多個(gè)任務(wù)的執(zhí)行順序
- 串行:多個(gè)任務(wù)一個(gè)挨著一個(gè)的有序執(zhí)行,上一個(gè)任務(wù)執(zhí)行完接著執(zhí)行下一個(gè)任務(wù)
- 并發(fā):多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行理茎,會(huì)自動(dòng)開(kāi)啟多個(gè)線程來(lái)同時(shí)執(zhí)行任務(wù)
總結(jié)如下圖:
接下來(lái)我們先用代碼驗(yàn)證同步執(zhí)行任務(wù)的情況黑界,代碼如下:
同步串行
void syncSerialTest() {
// 創(chuàng)建一個(gè)串行隊(duì)列,一個(gè)挨著一個(gè)的執(zhí)行任務(wù)
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
// 同步執(zhí)行任務(wù)皂林,在當(dāng)前主線程上執(zhí)行任務(wù)朗鸠,沒(méi)有開(kāi)啟新線程(不具備開(kāi)啟異步線程的能力)
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)1+++順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)2---順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
}
同步并發(fā)
void syncConcurrentTest() {
// 創(chuàng)建一個(gè)并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
// 同步執(zhí)行任務(wù),在當(dāng)前主線程上執(zhí)行任務(wù)础倍,沒(méi)有開(kāi)啟新線程(不具備開(kāi)啟異步線程的能力)
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)1+++順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)2---順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
}
異步串行
void asyncSerialTest() {
// 創(chuàng)建一個(gè)串行隊(duì)列烛占,一個(gè)挨著一個(gè)的執(zhí)行任務(wù)
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
// 異步執(zhí)行任務(wù)
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)1+++順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)2---順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
}
異步并發(fā)
void asyncConcurrentTest() {
// 創(chuàng)建一個(gè)并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
// 異步執(zhí)行任務(wù)
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)1+++順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)2---順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
}
從上面的測(cè)試代碼打印我們可以看到,當(dāng)我們使用dispatch_snyc
同步執(zhí)行任務(wù)沟启,任務(wù)在當(dāng)前主線程執(zhí)行忆家,沒(méi)有開(kāi)啟新的子線程,不管是串行隊(duì)列還是并發(fā)隊(duì)列德迹,最終任務(wù)都是一個(gè)挨一個(gè)的串行執(zhí)行
當(dāng)我們使用dispatch_async
異步執(zhí)行任務(wù)時(shí)芽卿,任務(wù)會(huì)在新開(kāi)啟的子線程中執(zhí)行,如果是串行隊(duì)列胳搞,多個(gè)任務(wù)還是一個(gè)挨一個(gè)的串行執(zhí)行卸例,如果是并發(fā)隊(duì)列称杨,則此時(shí)多個(gè)任務(wù)是并發(fā)同時(shí)執(zhí)行
注意:當(dāng)我們使用dispatch_async
異步執(zhí)行任務(wù),但是此時(shí)的隊(duì)列如果是dispatch_get_main_queue
主隊(duì)列筷转,則此時(shí)并沒(méi)有開(kāi)啟新的子線程列另,任務(wù)任然是在當(dāng)前主線程中執(zhí)行,代碼如下:
// 異步主隊(duì)列(dispatch_get_main_queue是一種特殊的串行隊(duì)列)
void asyncMainQueue() {
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)1---順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
dispatch_queue_t queue2 = dispatch_get_main_queue();
dispatch_async(queue2, ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"執(zhí)行任務(wù)2---順序%ld-%@", (long)i, [NSThread currentThread]);
}
});
}
總結(jié)如圖:
我們?cè)賮?lái)看一個(gè)使用dispatch_sync
造成線程死鎖的示例旦装,代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"任務(wù)1");
// 主隊(duì)列,串行隊(duì)列有FIFO特點(diǎn)摊滔,也就是先進(jìn)先出
dispatch_queue_t queue = dispatch_get_main_queue();
// 同步執(zhí)行任務(wù)阴绢,立即在當(dāng)前主線程執(zhí)行任務(wù),任務(wù)不執(zhí)行完后面的代碼就會(huì)不會(huì)執(zhí)行
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2");
});
NSLog(@"任務(wù)3");
}
上面的代碼就造成了線程死鎖艰躺,因?yàn)?code>dispatch_sync特點(diǎn)是要求任務(wù)在當(dāng)前主線程立即執(zhí)行完任務(wù)2呻袭,任務(wù)2不執(zhí)行完后面的代碼就不會(huì)執(zhí)行,但是當(dāng)前線程任務(wù)3還沒(méi)有執(zhí)行完腺兴,不能夠執(zhí)行任務(wù)2左电,這樣就導(dǎo)致了任務(wù)2等任務(wù)3執(zhí)行完,任務(wù)3等任務(wù)2執(zhí)行完页响,產(chǎn)生死鎖
注意:串行隊(duì)列有FIFO特點(diǎn)篓足,在本示例中,執(zhí)行viewDidLoad
函數(shù)也是當(dāng)前線程的一個(gè)任務(wù)闰蚕,是在任務(wù)2之前進(jìn)入到隊(duì)列中排隊(duì)執(zhí)行的任務(wù)
如圖:
我們將上面的dispatch_sync
同步執(zhí)行任務(wù)改為dispatch_async
異步執(zhí)行任務(wù)就不會(huì)產(chǎn)生死鎖栈拖,示例代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"任務(wù)1");
// 主隊(duì)列,串行隊(duì)列有FIFO特點(diǎn)没陡,也就是先進(jìn)先出
dispatch_queue_t queue = dispatch_get_main_queue();
// 異步執(zhí)行任務(wù)涩哟,不要求任務(wù)立即執(zhí)行
dispatch_async(queue, ^{
NSLog(@"任務(wù)1");
});
NSLog(@"任務(wù)3");
}
接下來(lái)我們?cè)賮?lái)看下線程組dispatch_group_t
的基本用法,假設(shè)我們需要實(shí)現(xiàn)下面這個(gè)需求:異步并發(fā)執(zhí)行任務(wù)1和任務(wù)2盼玄,等任務(wù)1和任務(wù)2都執(zhí)行完后在回到主線程執(zhí)行任務(wù)3贴彼,這里就可以借助線程組來(lái)實(shí)現(xiàn),代碼如下:
// 創(chuàng)建線程組
dispatch_group_t group = dispatch_group_create();
// 創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
// 添加任務(wù)1到group
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"任務(wù)1 == %zd ---%@",i, [NSThread currentThread]);
}
});
// 添加任務(wù)2到group
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"任務(wù)2 == %zd ---%@",i, [NSThread currentThread]);
}
});
// 等group前面的任務(wù)都執(zhí)行完后埃儿,再執(zhí)行notify中的任務(wù)3
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"任務(wù)3 == %zd ---%@",i, [NSThread currentThread]);
}
});
});
講解示例Demo地址:https://github.com/guangqiang-liu/09-GCD
更多文章
- ReactNative開(kāi)源項(xiàng)目OneM(1200+star):https://github.com/guangqiang-liu/OneM:歡迎小伙伴們 star
- iOS組件化開(kāi)發(fā)實(shí)戰(zhàn)項(xiàng)目(500+star):https://github.com/guangqiang-liu/iOS-Component-Pro:歡迎小伙伴們 star
- 簡(jiǎn)書(shū)主頁(yè):包含多篇iOS和RN開(kāi)發(fā)相關(guān)的技術(shù)文章http://www.reibang.com/u/023338566ca5 歡迎小伙伴們:多多關(guān)注器仗,點(diǎn)贊
- ReactNative QQ技術(shù)交流群(2000人):620792950 歡迎小伙伴進(jìn)群交流學(xué)習(xí)
- iOS QQ技術(shù)交流群:678441305 歡迎小伙伴進(jìn)群交流學(xué)習(xí)