Reputation: 2816
I can't figure out why the following code is being called out of order. It's only written out of order to expose the order in which they're called. This is important because I'm using these with web service calls that are supposed to wait on one another, and they're not. So, it may just be my conception of how GCD is used.
var group: dispatch_group_t = dispatch_group_create()
var groupTwo: dispatch_group_t = dispatch_group_create()
var queue: dispatch_queue_t = dispatch_get_main_queue()
dispatch_group_notify(groupTwo, queue) { () -> Void in
print("3rd") // Should be called 3rd
}
dispatch_group_enter(group)
print("1st") // Should be called 1st
dispatch_group_leave(group)
dispatch_group_notify(group, queue) { () -> Void in
dispatch_group_enter(groupTwo)
print("2nd") // Should be called 2nd
dispatch_group_leave(groupTwo)
}
This is the order it's printing in:
1st
3rd
2nd
Why is the order wrong? Shouldn't 3rd
only get called once dispatch_group_leave(groupTwo)
is called? Why is it getting called beforehand? I thought that was what dispatch_group_notify()
was used for.
EDIT: Sorry, I just fixed the group names. Forgot to edit some at first.
Upvotes: 1
Views: 115
Reputation: 1176
It sounds like you need to wait for the result of two or more remote calls before you can issue a final remote call. Let's say you have two properties for the parameters, which will be populated by remote calls:
var a:String? = nil // populated asynchronously
var b:String? = nil // populated asynchronously
Then, let's say your remote calls look like this:
func getParameterA( completionHandler:( String ) -> Void ) {
print( "Getting parameter A" )
dispatch_async( dispatch_get_global_queue( QOS_CLASS_USER_INITIATED, 0 ) ) {
NSThread.sleepForTimeInterval(0.2)
completionHandler( "A" )
print( "Got parameter A" )
}
}
func getParameterB( completionHandler:( String ) -> Void ) {
print( "Getting parameter B" )
dispatch_async( dispatch_get_global_queue( QOS_CLASS_USER_INITIATED, 0 ) ) {
NSThread.sleepForTimeInterval( 0.1 )
completionHandler( "B" )
print( "Got parameter B" )
}
}
func getResult( a:String, b:String, completionHandler:( String ) -> Void ) {
dispatch_async( dispatch_get_global_queue( QOS_CLASS_USER_INITIATED, 0 ) ) {
NSThread.sleepForTimeInterval( 0.05 )
completionHandler( "This is the result of \(a) and \(b)" )
}
}
In reality, they will make remote calls instead of sleeping on background threads.
Then, your code to populate a
and b
will look like this:
// the blocks in the parameter group are responsible for setting a and b
let parameterGroup = dispatch_group_create()
dispatch_group_enter( parameterGroup )
getParameterA() { parameter in
// set the value of a asynchronously
self.a = parameter
dispatch_group_leave( parameterGroup )
}
dispatch_group_enter( parameterGroup )
getParameterB() { parameter in
// set the value of b asynchronously
self.b = parameter
dispatch_group_leave( parameterGroup )
}
Finally, you can use dispatch_group_notify
to define a final completion handler that only executes once there are no more tasks in parameterGroup
:
let finalGroup = dispatch_group_create()
dispatch_group_enter( finalGroup )
dispatch_group_notify( parameterGroup, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) ) {
self.getResult( self.a!, b:self.b! ) { result in
print( "-- \(result)" )
dispatch_group_leave( finalGroup )
}
}
dispatch_group_wait( finalGroup, DISPATCH_TIME_FOREVER )
The finalGroup
is not strictly necessary, but I needed it to get the example to work inside an XCTest
.
The output will look like this:
Getting parameter A
Getting parameter B
Got parameter B
Got parameter A
-- This is the result of A and B
Upvotes: 1
Reputation: 19098
dispatch_group_notify(groupTwo, queue) { () -> Void in
print("3rd") // Should be called 3rd
}
dispatch_group_notify
will submit the first block to the queue when the group is empty. Initially, the group is empty. So, it will be asynchronously submitted to the main queue.
Here
dispatch_group_enter(group)
print("1st") // Should be called 1st
dispatch_group_leave(group)
you effectively print to the console on the main queue - this prints
1st
And here
dispatch_group_notify(group, queue) { () -> Void in
dispatch_group_enter(groupTwo)
print("2nd") // Should be called 2nd
dispatch_group_leave(groupTwo)
}
you asynchronously submit a second block which gets enqueued after the first block.
Now, the first block executes and prints
3rd
And finally, the second block prints:
2nd
Upvotes: 0