Wait not working for synchronous call

I’m doing something rather simple, but the execution doesn’t stop at the “wait”, it just continues so the data is incorrect. In the output I see the “After wait” message before the “Read the power….” message, and when the method returns it has the initialization value instead of the correct one. Can you tell me what I’m missing?


let dispatchGroup = DispatchGroup()


dispatchGroup.enter()

characteristic.readValue(completionHandler:

{ (error) in

if error != nil

{

print(error!.localizedDescription)

}

else

{

powerState = characteristic.value as! Int

print("CLightBulb: -->Read the power state. It is \(powerState) at " + Date().description(with: Locale.current))

}

})

dispatchGroup.leave()

dispatchGroup.wait()

print("CLightBulb: (After wait) The power value is \(powerState) at " + Date().description(with: Locale.current))

Replies

Have you put the whole in a user thread ?

See a good tutorial here: h ttps://www.raywenderlich.com/148515/grand-central-dispatch-tutorial-swift-3-part-2


DispatchQueue.global(qos: .userInitiated).async {
     let dispatchGroup = DispatchGroup()

     dispatchGroup.enter()
     characteristic.readValue(completionHandler:
      { (error) in
        if error != nil
          {
          print(error!.localizedDescription)
          }
         else
            {
              powerState = characteristic.value as! Int
              print("CLightBulb: -->Read the power state.  It is \(powerState) at " + Date().description(with: Locale.current))
            }
      })
dispatchGroup.leave()
dispatchGroup.wait()
  DispatchQueue.main.async { 
     print("CLightBulb: (After wait) The power value is \(powerState) at " + Date().description(with: Locale.current))
  }
}

Thanks... I did read that and several other explainations.


I tried what you suggested, and the results are the same:



CLightBulb: (After wait) The power value is 1

CLightBulb: -->Read the power state. It is 0

CLightBulb: (After wait) The power value is 1

CLightBulb: -->Read the power state. It is 0

Why do you get it twice ? (do you actionate the read twice ?)

Why don't you get de Date() in the printed log ?

You seem to have a nesting problem. Here’s your code, in a code block, with extraneous stuff cut away:

let dispatchGroup = DispatchGroup()

dispatchGroup.enter()
characteristic.readValue(completionHandler: { (error) in
    …
})
dispatchGroup.leave()
dispatchGroup.wait()

The problem is that you’re calling

leave()
on line 7, outside of the async block. Thus the sequence is:
  1. Create group
  2. Enter
  3. Start async
  4. Leave
  5. Wait, which completes immediately because there’s no pending Enter
  6. [some time later] Complete async

Instead you want this:

let dispatchGroup = DispatchGroup()

dispatchGroup.enter()
characteristic.readValue(completionHandler: { (error) in
    …
    dispatchGroup.leave()
})
dispatchGroup.wait()

Now the sequence is:

  1. Create group
  2. Enter
  3. Start async
  4. Wait, which blocks
  5. [some time later] Complete async
  6. Leave
  7. Wait unblocks

Having said that, converting async calls to sync calls like this is generally not recommended, especially with a dispatch group which doesn’t provide any sort of lock ownership. WWDC 2017 Session 706 Modernizing Grand Central Dispatch Usage talks about this in detail.

In my experience, when dealing with I/O like this you inevitably end up creating a state machine, and it’s better to address that concept directly rather than trying to force an inherently async situation into a synchronous box.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"