廢話time:
原本打算是從上一篇文章開始都用英文撰寫來練習英文寫作,但是打完上一篇文章後卻覺得,繁體中文資源已經不多,很多技術文件或討論文件在中文搜尋上根本是零零落落或乾脆搜尋不到相關資料,因此還是改變決定,繼續以中文撰寫,算是勉強盡一份心力於中文資源。
積存已久的問題,雖然早已解決,但是GCD的部分還是沒有很熟,還需要了解NSOperationQueue與NSThread的差別,現在主要在熟悉CS193p的文件,等未來有空再發一篇文章補齊三者的使用時機。
在main thread 使用NSURLConnection,建立一個thread等待資料庫讀完,再回到main thread執行一些code,在program裡面看起來如下:
//part.1
[NSURLConnection *myDB=[...];
//概要來說是使用POST Method, 送NSURLrequest以及設定delegate為self,
//在NSURLconnection的protocol - connection didRecievedata實現一個loadprogress
//part.2
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue,^{
while (myDB.loadprogress!=1){
//這裡只是等待progress=1, 就是讓data收完
}
dispath_async(dispatch_get_main_queue, ^{
//data收完,回到main thread去做事,例如轉換格式,取資料,填資料到tableview等
});
});
//part.3
//這邊繼續做main thread的事
現在問題在於,假使我希望part3在某個片段可以等待part2完成(收data)再繼續做事,比較好的寫法是什麼?
Solution:
GCD有group、serial或barrier的建立方式,都可以實現等待上個thread完成後再執行的效果。以下是一些簡單的implement:
barrier - 如同barrier的意思,會擋住下游的thread執行直到上游的thread執行完畢以及本身thread執行完畢,方讓下游的thread繼續執行。
dispatch_queue_t queue=dispatch_queue_create("flamelad", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i=0; i<3; i++) {
NSLog(@"concurrent:%d\n",i);
}
});
dispatch_barrier_async(queue, ^{
for (int i=0; i<3; i++) {
NSLog(@"barrier:%d\n",i);
}
});
dispatch_async(queue, ^{
for (int i=0; i<3; i++) {
NSLog(@"concurrent1:%d\n",i);
}
});
// 輸出結果:
// 2014-08-17 01:58:42.464 Sherlock[2836:1303] concurrent:0
// 2014-08-17 01:58:42.465 Sherlock[2836:1303] concurrent:1
// 2014-08-17 01:58:42.466 Sherlock[2836:1303] concurrent:2
// 2014-08-17 01:58:42.467 Sherlock[2836:1303] barrier:0
// 2014-08-17 01:58:42.468 Sherlock[2836:1303] barrier:1
// 2014-08-17 01:58:42.469 Sherlock[2836:1303] barrier:2
// 2014-08-17 01:58:42.470 Sherlock[2836:1303] concurrent1:0
// 2014-08-17 01:58:42.470 Sherlock[2836:1303] concurrent1:1
// 2014-08-17 01:58:42.471 Sherlock[2836:1303] concurrent1:2
serial- 與concurrent不同(很明顯吧XD),queue中的每一個thread會循序處理,FIFO
dispatch_queue_t serial=dispatch_queue_create("flamelad", NULL);
dispatch_async(serial, ^{
for (int i=0; i<3; i++) {
NSLog(@"serial:%d\n",i);
}
});
dispatch_async(serial, ^{
for (int i=0; i<3; i++) {
NSLog(@"serial2:%d\n",i);
}
});
dispatch_async(serial, ^{
for (int i=0; i<3; i++) {
NSLog(@"serial3:%d\n",i);
}
});
// 輸出結果
// 2014-08-17 02:19:43.616 Sherlock[2898:1303] serial:0
// 2014-08-17 02:19:43.626 Sherlock[2898:1303] serial:1
// 2014-08-17 02:19:43.643 Sherlock[2898:1303] serial:2
// 2014-08-17 02:19:43.644 Sherlock[2898:1303] serial2:0
// 2014-08-17 02:19:43.646 Sherlock[2898:1303] serial2:1
// 2014-08-17 02:19:43.648 Sherlock[2898:1303] serial2:2
// 2014-08-17 02:19:43.649 Sherlock[2898:1303] serial3:0
// 2014-08-17 02:19:43.650 Sherlock[2898:1303] serial3:1
// 2014-08-17 02:19:43.651 Sherlock[2898:1303] serial3:2
group - group有notify和wait方式可以實現類似barrier的效果,不同處在於可以做一些細節上的設定。從輸出結果即可知wait會等待上游thread完成,notify則會等到group中所有thread完成。
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,^{
for (int i=0; i<3; i++) {
NSLog(@"group:%d\n",i);
}
});
dispatch_group_async(group,queue,^{
for (int i=0; i<3; i++) {
NSLog(@"group2:%d\n",i);
}
});
// 可設定等待下游的thread等待上游thread多久時間後開始執行,FOREVER則是等到上游執行完成後。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_group_async(group,queue,^{
for (int i=0; i<3; i++) {
NSLog(@"group3:%d\n",i);
}
});
dispatch_group_notify(group,queue,^{
for (int i=0; i<3; i++) {
NSLog(@"group4:%d\n",i);
}
});
dispatch_group_async(group,queue,^{
for (int i=0; i<3; i++) {
NSLog(@"group5:%d\n",i);
}
});
// 輸出結果
// 2014-08-17 02:32:54.587 Sherlock[3001:130b] group:0
// 2014-08-17 02:32:54.587 Sherlock[3001:3c07] group2:0
// 2014-08-17 02:32:54.588 Sherlock[3001:130b] group:1
// 2014-08-17 02:32:54.588 Sherlock[3001:3c07] group2:1
// 2014-08-17 02:32:54.590 Sherlock[3001:130b] group:2
// 2014-08-17 02:32:54.591 Sherlock[3001:3c07] group2:2
// 2014-08-17 02:32:54.591 Sherlock[3001:130b] group3:0
// 2014-08-17 02:32:54.592 Sherlock[3001:3c07] group5:0
// 2014-08-17 02:32:54.593 Sherlock[3001:130b] group3:1
// 2014-08-17 02:32:54.593 Sherlock[3001:3c07] group5:1
// 2014-08-17 02:32:54.594 Sherlock[3001:130b] group3:2
// 2014-08-17 02:32:54.595 Sherlock[3001:3c07] group5:2
// 2014-08-17 02:32:54.596 Sherlock[3001:3c07] group4:0
// 2014-08-17 02:32:54.597 Sherlock[3001:3c07] group4:1
// 2014-08-17 02:32:54.598 Sherlock[3001:3c07] group4:2
這篇只介紹了三種最常用到的GCD thread執行方式來解決我的問題,但其實尚有些如dispatch_once、dispatch_semaphore_t等的執行方式,有興趣者再自行姑狗,都是在實現multi-thread石,可能會用到的方法。