2015-04-12 125 views
3

我有一個異步函數塊:的iOS GCD同步與異步模塊

[self performAsyncTaskCompletion:(void(^)()) 
{ 
    //Do Something 
} 
]; 

我需要調用這個函數很多次,但以同步的方式。我試圖使用GCD隊列:

dispatch_queue_t queue = dispatch_queue_create("com.MyApp.task", NULL); 
for (int i = 0; i < array.count; i++) 
{ 
    dispatch_sync(queue, ^{ 
    [self performAsyncTaskCompletion:(void(^)()) 
     { 
     //Do Something 
     } 
     ]; 
    }); 
} 

但它不工作,因爲dispatch_sync只是在等待塊的結尾。 如何讓它等待塊中的異步函數結束?

+0

這些'performAsyncTaskCompletion'塊可以相互並行運行嗎?或者你需要等待前一個完成。另外,我知道你在等待塊的末尾,但這總是錯誤的模式。通常我們讓它異步運行,但是隻需指定一個代碼塊,當所有其他調度塊完成時,它將運行。那對你有用嗎? – Rob

+0

是的,那就是我想要做的。實際上,我想從Web服務器下載(異步)一堆不同的JSON文件。想象一下,每個JSON都會引導我去下載另一個JSON。有可能我已經下載了下一個JSON,所以我只需要執行同步任務就可以從數組中獲取它。所以,JSON下載應該是同步的。最後,我需要一個塊,說「好吧,一切都被下載」。我應該使用什麼樣的模式? – Aladin

+0

你說「想象每個JSON引導我到另一個」。你是說每個人都真正依賴前一個?通常情況下,人們希望最大限度地提高併發性,但是如果每個JSON都需要來自之前的數據,那就限制了您。 – Rob

回答

7

你可以使用調度組,如果你想啓動一些在完成一系列異步任務之後,但想允許這些任務相對於彼此(其中,尤其是網絡請求,可以提供更好的性能比依次運行這些)同時運行:個人

dispatch_group_t group = dispatch_group_create(); 

for (int i = 0; i < array.count; i++) { 
    dispatch_group_enter(group); 
    [self performAsyncTaskCompletion: ^{ 
     //Do Something 
     dispatch_group_leave(group); 
    }]; 
} 

dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
    // do this when its all done 
}); 

,我甚至可能會傾向於執行performAsyncTaskCompletion的更爲徹底的重構,改爲使用異步NSOperation子類型。然後,您可以將這些添加到指定的maxConcurrentOperationCountNSOperationQueue,從而實現相同的併發性,同時控制併發程度。但希望上面的例子說明了這個想法:併發運行任務,但檢測到這些任務的完成而不會阻塞主線程。

+0

謝謝:) 'performAsyncTaskCompletion'依賴於AFNetworking,我不能真正修改它。 – Aladin

+1

首先,如果您使用的是「AFHTTPRequestOperationManager」或「AFHTTPRequestOperation」,那麼您已經使用基於操作隊列的解決方案(然後我會爲完成操作設置標準的'NSOperation'依賴項)。即使您使用的是「AFHTTPSessionManager」,您仍然可以將這些包裝在自己的「NSOperation」子類中。這些都不需要修改AFNetworking,而只需執行'performAsyncTaskCompletion'。無論如何,通知方法仍然有效。但是,不要同時做這個東西。 – Rob

1

您可以使用dispatch_async與semaphores

例子:

- (void)performAsyncTaskCompletion:(void (^)())block { 
    if (block) { 
     block(); 
    } 
} 

- (void)runChainOfOperations { 
    static dispatch_once_t onceToken; 
    static dispatch_semaphore_t semaphore; 
    static dispatch_queue_t queue; 
    dispatch_once(&onceToken, ^{ 
     semaphore = dispatch_semaphore_create(1); 
     queue = dispatch_queue_create("com.MyApp.task", NULL); 
    }); 

    NSArray *array = @[@1, @2, @3, @4, @5]; 
    static long counter = 0; 
    for (int i = 0; i < array.count; i++) { 
     dispatch_async(queue, ^{ 
      dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
      [self performAsyncTaskCompletion:^{ 
       sleep(10); 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        NSLog(@"%ld", counter++); 
       }); 
       dispatch_semaphore_signal(semaphore); 
      }]; 
     }); 
    } 

}

控制檯輸出:

2015-04-12 21:28:06.047 HKTest[9497:1136830] 0 
2015-04-12 21:28:16.023 HKTest[9497:1136830] 1 
2015-04-12 21:28:26.025 HKTest[9497:1136830] 2 
2015-04-12 21:28:36.029 HKTest[9497:1136830] 3 
2015-04-12 21:28:46.031 HKTest[9497:1136830] 4 
+0

非常好的答案!您可以告訴我如何在異步任務完成時從runChainOfOperations返回一個塊嗎? – Aladin

+0

您將在for循環之後再添加一個dispatch_async塊。 – ninjaproger

+0

我認爲你的例子給出了一個錯誤的方式來使用'信號量',沒有有用的'dispatch_semaphore_wait'在你的例子 –