在軟件開發中使用多線程能夠大大的提高用戶體驗度,增長工做效率。iOS系統中提供了多種分線程編程的方法,在前兩篇博客都有說起:編程
NSThread類進行多線程編程:http://my.oschina.net/u/2340880/blog/416524。多線程
NSOperation進行多線程操做編程:http://my.oschina.net/u/2340880/blog/416782。併發
上兩個進行多線程編程的機制都是封裝於Object-C的類與方法。這篇博客將討論的Grand Central Dispatch(GCD)機制,則是基於C語言的,相比上面兩種機制,GCD更加高效,而且線程有系統管理,會自動運用多核運算。由於這些優點,GCD是apple推薦咱們使用的多線程解決方案。app
GCD機制中一個很重要的概念是調度隊列,咱們對線程的操做其實是由調度隊列完成的。咱們只須要將要執行的任務添加到合適的調度隊列中便可。框架
調度隊列有三種類型:異步
(1)主隊列async
其中的任務在主線程中執行,由於其會阻塞主線程,因此這是一個串行的隊列。能夠經過dispatch_get_main_queue()方法獲得。ide
(2)全局並行隊列函數
隊列中任務的執行方式是嚴格按照先進先出的模式進行了。若是是串行的隊列,則當一個任務結束後,纔會開啓另外一個任務,若是是並行隊列,則任務的開啓順序是和添加順序一致的。系統爲iOS應用自動建立了四個全局共享的併發隊列。使用以下函數得到:性能
dispatch_get_global_queue(long identifier, unsigned long flags);
其中第一個參數是這個隊列的id,系統的四個全局隊列默認的優先級不一樣,這個參數可填的定義以下:
#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//後臺的全局隊列 優先級最低
這個函數的第二個參數,按照官方文檔的說法是有待將來使用,如今咱們都填0便可。
(3)自定義隊列
上面的兩種隊列都是系統爲咱們建立好的,咱們只須要獲取到他們,將任務添加便可。固然,咱們可能夠建立咱們本身的隊列,包括串行的和並行的。使用以下方法建立:
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
其中,第一個參數是這個隊列的名字,第二個參數決定建立的是串行的仍是並行的隊列。填寫DISPATCH_QUEUE_SERIAL或者NULL建立串行隊列,填寫DISPATCH_QUEUE_CONCURRENT建立並行隊列。
使用dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)函數或者dispatch_async(dispatch_queue_t queue, dispatch_block_t block)函數來同步或者異步的執行任務,示例以下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ NSLog(@"%@:1",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@:2",[NSThread currentThread]); }); }
打印結果以下:
能夠看出第一個任務在主線程中執行,第二個在分線程中執行。
經過上面的演示,咱們已經能夠運用隊列進行多線程的執行任務,可是GCD的強大之處遠遠不止如此。
若是有這樣三個任務,A與B是沒有關係的,他們能夠並行執行,C必須在A,B結束以後才能執行,固然,實現這樣的邏輯並不困難,使用KVO就能夠實現,可是使用隊列組處理這樣的邏輯,代碼會更加清晰簡單。
可使用dispatch_group_create()建立一個隊列組,使用以下函數將隊列添加到隊列組中:
void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
隊列組中的隊列是異步執行的,示例以下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //建立一個隊列組 dispatch_group_t group=dispatch_group_create(); 建立一個異步隊列 dispatch_queue_t queue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_group_async(group, queue, ^{ for (int i=0; i<10; i++) { NSLog(@"%@:%d",[NSThread currentThread],i); } }); dispatch_group_async(group, queue, ^{ for (int i=0; i<10; i++) { NSLog(@"%@:%d",[NSThread currentThread],i); } }); //阻塞線程直到隊列任務完成 dispatch_group_wait(group,DISPATCH_TIME_FOREVER); for (int i=0; i<10; i++) { NSLog(@"over:%d",i); } }
打印出來的信息以下:
能夠看出,隊列中的任務是異步執行的,而且等待隊列組中隊列任務所有執行後才執行後面的任務。這樣的作法在實際應用中咱們不多使用,一般咱們會把後續的任務在放在異步中執行,作法以下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //建立一個隊列組 dispatch_group_t group=dispatch_group_create(); //建立一個隊列 dispatch_queue_t queue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); //添加隊列任務到隊列組 dispatch_group_async(group, queue, ^{ for (int i=0; i<10; i++) { NSLog(@"%@:%d",[NSThread currentThread],i); } }); dispatch_group_async(group, queue, ^{ for (int i=0; i<10; i++) { NSLog(@"%@:%d",[NSThread currentThread],i); } }); //隊列組任務執行完後執行的任務 dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (int i=0; i<10; i++) { NSLog(@"over:%d",i); } }); for (int i=0; i<10; i++) { NSLog(@"Finish:%d",i); } }
打印信息以下:
能夠看出GCD的強大了吧,複雜的任務邏輯關係由於GCD變得十分清晰簡單。
一開始咱們就提到,GCD相比NSOperation的優點在於多核心的應用,更深得挖掘出了硬件的性能。GCD在多核方面的一個明顯的特色就是循環機制。
dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) { NSLog(@"%@:%zu",[NSThread currentThread],i); });
打印結果以下:
能夠看出,程序的運行效率又會高許多。
dispatch_source_t類型的對象能夠用來傳遞和接受某個消息,而後執行block方法,示例以下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //建立一個數據對象,DISPATCH_SOURCE_TYPE_DATA_ADD的含義表示數據變化時相加 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue()); //建立接收數據變化的句柄 dispatch_source_set_event_handler(source, ^{ NSLog(@"%lu",dispatch_source_get_data(source)); }); //啓動 dispatch_resume(source); //設置數據 dispatch_source_merge_data(source, 1); //這步執行完以後會執行打印方法 }
GCD中還有一個重要的概念是信號量。它的用法法消息的傳遞有所相似,經過代碼來解釋:
//建立一個信號,其中的參數爲信號的初始值 dispatch_semaphore_t singer = dispatch_semaphore_create(0); //發送信號,使信號量+1 dispatch_semaphore_signal(singer); //等待信號,當信號量大於0時執行後面的方法,不然等待,第二個參數爲等待的超時時長,下面設置的爲一直等待 dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER); NSLog(@"123");
經過發送信號,能夠試信號量+1,每次執行過等待信號後,信號量會-1;如此,咱們能夠很方便的控制不一樣隊列中方法的執行流程。
GCD還提供了暫停與開始任務的方法,使用
void dispatch_suspend(dispatch_object_t object);
能夠將隊列或者隊列組進行暫時的掛起,使用
void dispatch_resume(dispatch_object_t object);
將隊列或者隊列組從新開啓。
須要注意的是,暫停隊列時,隊列中正在執行的任務並不會被中斷,會掛起未開啓的任務。
GCD雖然是基於C語言封裝的框架,使用了面向對象的思想。所以,它的內存管理是須要咱們注意的,不管是ARC或者MRC,咱們都應該手動去處理這些對象。還好,GCD的內存管理思路和Object—C是兼容的,咱們使用dispatch_retain()和dispatch_release()來將引用對象的計數進行加減。這一點十分重要,切記切記。
疏漏之處 歡迎指正
學習使用 歡迎轉載
專一技術,熱愛生活,交流技術,也作朋友。
——琿少 QQ羣:203317592