[讀書筆記]iOS與OS X多執行緒和記憶體管理 [GCD部分]
蘋果對GCD的說明:開發者要做的只是定義想執行的任務並追加到適當的Dispatch Queue中。 “Dispatch Queue”是執行處理的等待佇列。通過dispatch_async函式等API,在Block語法中記述想執行的處理並追加到Dispatch Queue中,Dispatch Queue按照追加的順序,執行處理。 Dispatch Queue分為兩種:
種類 |
說明 |
Serial Dispatch Queue | 等待現在執行中處理結束(順序執行) |
Concurrent Dispatch Queue | 不等待現在執行中處理結束(併發執行) |
生成Dispatch Queue的方式有兩種 第一通過GCD的API生成Dispatch Queue;
dispatch_queue_t
myQu=dispatch_queue_create("log", DISPATCH_QUEUE_CONCURRENT);
|
dispatch_queue_t myQU=dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); dispatch_async(myQU, ^{ NSLog(@"test"); }); dispatch_release(myQU); |
第二種方法是獲取系統標準提供的Dispatch Queue。
名稱 | 種類 | 說明 |
Main Dispatch Queue | Serial Dispatch Queue | 主執行緒執行 |
Global Dispatch Queue |
Concurrent Dispatch Queue |
有四個等級:High、Default、Low、Background。分別對應高、預設、低、後臺 |
獲取方法
名稱 | 獲取方法 |
Main Dispatch Queue |
dispatch_queue_t
mainQU=dispatch_get_main_queue(); |
Global Dispatch Queue |
dispatch_queue_t
globalQU=dispatch_get_global_queue(<#dispatch_queue_priority_t priority#>, 0);
第一個引數代表等級,分別有:
#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吧。 |
dispatch_async
(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ /** * 並行執行的處理 */ //需要在主執行緒展處理的,例如介面更新 dispatch_async(dispatch_get_main_queue(), ^{ /** * 主執行緒處理 */ }); }); |
3.2.4 dispatch_set_target_queue
我們自己生成的Dispatch Queue都是使用與Global Dispatch Queue預設優先順序相同優先順序的執行緒,變更Dispatch Queue的優先順序要使用dispatch_set_target_queue函式。
dispatch_queue_t mySerialQueue=dispatch_queue_create("test_serial", NULL); dispatch_queue_t globalBackground=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); dispatch_set_target_queue(mySerialQueue, globalBackground); |
3.2.5 dispatch_after
dispatch_after在指定的時間之後將Block追加到Dispatch Queue。
函式:
void
dispatch_after(dispatch_time_t when,
dispatch_queue_t
queue,dispatch_block_t block);
使用:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@“after");
});
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta); dispatch_time_t dispatch_walltime(const struct timespec *when, int64_t delta); timespec結構體可以通過NSDate型別的資料生成。 |
3.2.6 Dispatch Group
如果想等待追加到Dispatch Queue中的多個處理結束後再執行某些結束處理,可以選擇使用Serial Dispatch Queue,也可以使用Dispatch Group 配合 Concurrent Dispatch Queue的方式。 使用例項:
dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group=dispatch_group_create();
dispatch_group_async(group, globalQueue, ^{NSLog(@"0");}); dispatch_group_async(group, globalQueue, ^{NSLog(@"1");}); dispatch_group_async(group, globalQueue, ^{NSLog(@"2");}); dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"done");}); |
dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group=dispatch_group_create(); dispatch_group_async(group, globalQueue, ^{NSLog(@“0");}); dispatch_group_async(group, globalQueue, ^{NSLog(@"1");}); dispatch_group_async(group, globalQueue, ^{NSLog(@"2");}); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); |
3.2.7 dispatch_barrier_async
本節參考蘋果官網有改動。 dispatch_barrier_async函式同dispatch_queue_create函式生成的Concurrent Dispatch Queue一起使用。一個比較合適的場景是對資料庫的操作,不應在同一時刻對資料庫進行多個寫操作,但是多個讀操作是允許的,此時的寫操作的實現是比較複雜的。 dispatch_barrier_async函式的作用就是會等待追加到 Concurrent Dispatch Queue處理全部結束後再將指定的處理追加到該 Concurrent Dispatch Queue。並不等待Block被呼叫函式就返回,等 dispatch_barrier_async函式追加到 Concurrent Dispatch Queue處理執行結束後, dispatch_barrier_async 追加到 Concurrent Dispatch Queue的處理又開始執行。比較適合資料庫和檔案訪問。
dispatch_queue_t concurrentQU=dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQU, ^{
NSLog(@"write data0");});
dispatch_async(concurrentQU, ^{ NSLog(@"write data1");});
dispatch_async(concurrentQU, ^{ NSLog(@"write data2"); });
dispatch_async(concurrentQU, ^{ NSLog(@"write data3");});
dispatch_barrier_async(concurrentQU, ^{
NSLog(@"read...");
});
dispatch_async(concurrentQU, ^{ NSLog(@"write data4”); });
dispatch_async(concurrentQU, ^{ NSLog(@"write data5”); });
執行結果是: write data1 write data0 write data2 write data3 read... write data4 write data5 |
dispatch_barrier_async
Submits a barrier block for asynchronous execution and returns immediately. DeclarationObjective-C
DiscussionCalls to this function always return immediately after the block has been submitted and never wait for the block to be invoked. When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes. The queue you specify should be a concurrent queue that you create yourself using the |
dispatch_barrier_sync,此函式與
dispatch_barrier_async函式的區別是它會等待它所提交的Block執行完畢才返回,也就是說會阻塞執行緒,等Block執行完畢後函式後面的任務才會被提交到程序。個人理解是,
dispatch_barrier_async提交任務後函式之後的任務也被提交,但並未執行,等函式提交的任務執行後,在其之後提交的任務才執行,而dispatch_barrier_sync函式提交任務後知道任務執行結束才接著往下執行。
dispatch_barrier_async使用率較高。
官網說明如下:
dispatch_barrier_sync
Submits a barrier block object for execution and waits until that block completes. DeclarationObjective-C
Parameters
DiscussionSubmits a barrier block to a dispatch queue for synchronous execution. Unlike When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the queue executes the barrier block by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes. The queue you specify should be a concurrent queue that you create yourself using the Unlike with As an optimization, this function invokes the barrier block on the current thread when possible. |
3.2.8 dispatch_sync
dispatch_async意味著“非同步”(asynchronous),與之相對應的就是同步(synchronous),即dispatch_sync函式,將指定的Block“同步“追加到指定的Dispatch Queue中,在追加Block結束之前,函式會一直等待。可以說 dispatch_sync是簡易版的 dispatch_group_wait函式。dispatch_sync容易引起死鎖,下面的程式碼在主執行緒中執行就會死鎖。
dispatch_queue_t queue=dispatch_get_main_queue(); dispatch_sync(queue, ^{ NSLog(@"hello"); }); 該原始碼在主執行緒中執行指定的Block,並等待其執行結束,然而主執行緒正在執行原始碼,已經被阻塞,無法繼續執行任何原始碼。 |
3.2.9 dispach_apply
dispatch_apply函式和Dispatch Group關聯的API。該函式按指定的次數將指定的Block追加到某個Dispatch Queue中,並等待處理全部結束。
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(5, queue, ^(size_t index) { NSLog(@"%zu",index); }); NSLog(@"done"); 執行結果:1 0 3 2 4 done |
3.2.10 dispatch_suspend/dispatch_resume
dispatch_suspand掛起指定的Dispatch Queue,dispatch_resume恢復指定的Dispatch Queue。
3.2.11 Dispatch Semaphore
Dispatch Semaphore是持有計數的訊號,該計數是多執行緒程式設計中計數型別訊號。
dispatch_semaphore_t semaphore=dispatch_semaphore_create(1);//引數表示計數的初始值 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//此函式等待計數值等於或大於1,對該技術進行減法並返回,第二個引數是等待時間,若在等待時間內semaphore的計數滿足要求,則返回0,反之返回非0; dispatch_semaphore_signal(semaphore);// 該函式使 semaphore 加 1 ; |
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //生成計數訊號(訊號量),初始值為1,保證同時訪問陣列的執行緒只有一個。 dispatch_semaphore_t sema=dispatch_semaphore_create(1); NSMutableArray*array=[[NSMutableArray alloc]init]; for (int i=0; i<10000; i++) { dispatch_async(queue, ^{ //等待計數訊號直到大於等於1 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); /** * 由於第一個訪問的執行緒訪問時,訊號量等於1,滿足條件,wait函式返回,訊號量為0,其餘訪問的執行緒只能等待,能訪問array的執行緒始終只有一個 */ [array addObject:[NSNumber numberWithInt:i]]; //排他控制結束,訊號量加1 dispatch_semaphore_signal(sema); }); } |
3.2.12 dispatch_once dispatch_once是保證在應用程式執行中只執行一次的API。
static dispatch_once_t pred;
dispatch_once(&pred, ^{ /** * 初始化程式碼 */ }); |
3.2.13 Dispatch I/O
Dispatch I/O和Dispatch Data 能實現讀取檔案時將檔案分成合適大小使用Global Dispatch Queue將一個檔案按某個大小讀取。如:
dispatch_async(queue, ^{/*讀取0~8191位元組*/}); dispatch_async(queue, ^{/*讀取8192~16383位元組*/}); dispatch_async(queue, ^{/* 讀取 16383~24575 位元組 */ }); |
蘋果官方使用示例:
pipe_q = dispatch_queue_create("PipeQ", NULL);
pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){
close(fd);
});
*out_fd = fdpair[1];
dispatch_io_set_low_water(pipe_channel, SIZE_MAX);
dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){
if (err == 0)
{
size_t len = dispatch_data_get_size(pipedata);
if (len > 0)
{
const char *bytes = NULL;
char *encoded;
dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len);
encoded = asl_core_encode_buffer(bytes, len);
asl_set((aslmsg)merged_msg, ASL_KEY_AUX_DATA, encoded);
free(encoded);
_asl_send_message(NULL, merged_msg, -1, NULL);
asl_msg_release(merged_msg);
dispatch_release(md);
}
}
if (done)
{
dispatch_semaphore_signal(sem);
dispatch_release(pipe_channel);
dispatch_release(pipe_q);
}
});
|
3.3 GCD實現 略