iOS實(shí)際上算是unix的一個(gè)分支白粉,所以iOS上的多線程可以使用pthread。不過(guò)Apple另外提供了GCD來(lái)簡(jiǎn)化多線程編程鼠渺,實(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扶供,目前沒(méi)有使用這個(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ì)沒(méi)有輸出結(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é)果,不過(guò)可能會(huì)有點(diǎn)小問(wèn)題,如果是在主線程中執(zhí)行上面的代碼鞭莽,block done結(jié)果并沒(méi)有看到坊秸。這是因?yàn)橹骶€程的queue是Serial的,新任務(wù)要等主線程結(jié)束才會(huì)執(zhí)行澎怒。
dispatch_group_notify(group, queue, ^{printf("block done\n");});
修改之后就沒(méi)有問(wèn)題了褒搔。dispatch_group_notify
、dispatch_group_wait
函數(shù)的使用就不再過(guò)多的說(shuō)明了喷面。
通常情況下星瘾,使用多線程要注意讀寫鎖。讀可以多個(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è)線程安全的單列了?這是面試中常問(wèn)的一個(gè)問(wèn)題叨咖。GCD中使用dispatch_once
可以簡(jiǎn)單高效的完成這一類任務(wù)瘩例。
static dispatch_once_t classA_once;
dispatch_once(&classA_once,^
{
//initialization
});
GCD中還有諸如Semaphore、I/O的內(nèi)容甸各,不過(guò)實(shí)際中這些內(nèi)容使用的比較少垛贤,就在此跳過(guò)了。使用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
也沒(méi)有想我們想象的一樣執(zhí)行誊酌。
如有錯(cuò)誤部凑,歡迎指出!