Any way to ensure GCD tasks finish in order without serial queues?
grand-central-dispatch, objective-c
Solution
Each of your completion blocks (except the very first) has two dependencies: the heavy-lifting job and the completion block of the prior heavy-lifting job.
It will be much simpler to meet your requirements using `NSOperationQueue` and `NSBlockOperation` instead of using GCD directly. (`NSOperationQueue` is built on top of GCD.)
You need an operation queue and a reference to the prior completion operation:
@property (nonatomic, strong) NSOperationQueue *queue;
@property (nonatomic, strong) NSOperation *priorCompletionOperation;
Initialize the `queue` to an `NSOperationQueue`. Leave `priorCompletionOperation` nil until you get the first job.
Then it's just a matter of setting up your dependencies before submitting the operations to the queues:
NSBlockOperation *heavyLifting = [NSBlockOperation blockOperationWithBlock:^{
// long-running code here of varying complexity
}];
NSBlockOperation *completion = [NSBlockOperation blockOperationWithBlock:^{
// Callback here
}];
[completion addDependency:heavyLifting];
if (self.priorCompletionOperation) {
[completion addDependency:self.priorCompletionOperation];
}
[self.queue addOperation:heavyLifting];
[[NSOperationQueue mainQueue] addOperation:completion];
self.priorCompletionOperation = completion;
Note that you should make sure this job-queuing code only runs from a single thread at a time. If you only enqueue jobs from the main thread (or main queue) that will happen automatically.
Problem
I'm using GCD to do some heavy lifting - image manipulation and so on - often with 3 or 4 tasks running concurrently. Some of these tasks complete more quickly than others. How do I ensure that the callbacks are fired in the correct, original order - without using a serial queue? For example: - Task one takes 1 second - Task two takes 5 seconds - Task three takes 2 seconds How do I ensure the final callback order of one, two, three - despite the varied computation time? ``` // self.queue = dispatch_queue_create("com.example.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(self.queue, ^{ // Long-running code here of varying complexity dispatch_async(dispatch_get_main_queue(), ^{ // Callback here }); }); ``` Edit: As per the comments, the first notification should go out as soon as Task One completes, even if the remaining tasks are processing. When Task Three completes, it should hold until Task Two is complete, then first off notifications for Two and Three in rapid succession. I'm thinking some kind of mutable array for pushing and shifting tasks could work. Is there a cleaner way though?