iOS實(shí)際上算是unix的一個(gè)分支,所以iOS上的多線程可以使用pthread。不過Apple另外提供了GCD來簡化多線程編程,實(shí)際上GCD是基于pthread的。大部分情況下使用iOS多線程都是和I/O相關(guān)的,需要記住UI相關(guān)的操作必須是單線程(main thread)的。
在GCD中我們并不是直接創(chuàng)建線程,而是使用queue(隊(duì)列)。GCD中的queue分為兩類:Serial和Concurrent,前者是需要等待前一個(gè)任務(wù)結(jié)束的,后者并不需要(任務(wù)執(zhí)行順序只有上帝才知道)。我們可以使用 dispatch_queue_create函數(shù)創(chuàng)建一個(gè)queue。
//默認(rèn)是Serial
dispatch_queue_create("ax_serial_queue", NULL/*DISPATCH_QUEUE_SERIAL*/);
dispatch_queue_create("ax_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
也可以直接獲取iOS為我們提供的queue。iOS為我們提供了兩個(gè)queue: ** Main Dispatch Queue ** (主線程)、Global Dispatch Queue,前者是Serial后者是Concurrent。其中Global Dispatch Queue有四種運(yùn)行級(jí)別:high、default、low、background。
dispatch_get_main_queue()
//下面的第二個(gè)參數(shù)一般為0,目前沒有使用這個(gè)參數(shù)。
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
創(chuàng)建/獲取了queue之后就可以向queue中添加任務(wù),常用的是dispatch_async,這個(gè)是異步的,即不等待任務(wù)完成,dispatch_sync是同步的,即等待任務(wù)完成。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^
{
printf("block 1\n"):
});
直接執(zhí)行上面的代碼很可能會(huì)沒有輸出結(jié)果,:-D主線程已經(jīng)結(jié)束了(可以使用sleep試試)。為了更方便地管理,我們可以使用group。group使用dispatch_group_create()直接創(chuàng)建,然后將上面的dispatch_async改為dispatch_group_async即可。
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group=dispatch_group_create();
dispatch_group_async(group, queue, ^{printf("block1\n");});
dispatch_group_async(group, queue, ^{printf("block2\n");});
dispatch_group_async(group, queue, ^{printf("block3\n");});
dispatch_group_async(group, queue, ^{printf("block4\n");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{printf("block done\n");});
/*也可以試試下面的代碼
dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
printf("block done\n");*/
上面的代碼可以等待任務(wù)全部完成并輸出結(jié)果,不過可能會(huì)有點(diǎn)小問題,如果是在主線程中執(zhí)行上面的代碼,block done結(jié)果并沒有看到。這是因?yàn)橹骶€程的queue是Serial的,新任務(wù)要等主線程結(jié)束才會(huì)執(zhí)行。
dispatch_group_notify(group, queue, ^{printf("block done\n");});
修改之后就沒有問題了。dispatch_group_notify、dispatch_group_wait函數(shù)的使用就不再過多的說明了。
通常情況下,使用多線程要注意讀寫鎖。讀可以多個(gè)線程一起讀,寫只能一個(gè)線程寫。在多個(gè)dispatch_async中插入dispatch_barrier_async就可以實(shí)現(xiàn)pthread的讀寫鎖的效果。
dispatch_async(concurrent_queue, ^{printf("1\n");});
dispatch_async(concurrent_queue, ^{printf("2\n");});
dispatch_barrier_async(concurrent_queue, ^{printf("3\n");});
dispatch_async(concurrent_queue, ^{printf("4\n");});
dispatch_async(concurrent_queue, ^{printf("5\n");});
執(zhí)行順序是1/2 => 3 =>4/5,1和2的順序不定,4和5的順序不定,3必然在1、2之后以及4、5之前。
如何寫一個(gè)線程安全的單列了?這是面試中常問的一個(gè)問題。GCD中使用dispatch_once可以簡單高效的完成這一類任務(wù)。
static dispatch_once_t classA_once;
dispatch_once(&classA_once,^
{
//initialization
});
GCD中還有諸如Semaphore、I/O的內(nèi)容,不過實(shí)際中這些內(nèi)容使用的比較少,就在此跳過了。使用GCD進(jìn)行多線程編程需要我們注意的就是死鎖,比如在主線程中運(yùn)行dispatch_sync(dispatch_get_main_queue(),^{......});會(huì)直接導(dǎo)致程序crash這樣的嚴(yán)重后果。我們需要慎重地使用dispatch_sync函數(shù),上面group案例中的在dispatch_get_main_queue中使用dispatch_async也沒有想我們想象的一樣執(zhí)行。
如有錯(cuò)誤,歡迎指出!