Home > Enterprise >  DispatchGroup notify before all leave called
DispatchGroup notify before all leave called

Time:12-22

I have an iOS application that runs multiple classes(every class in a separate thread) and I want to get a notification when all the class finishes their run.

So I have a BaseClass:

class BaseClass {
    var completeState: CompleteState
    
    init(completeState: CompleteState) {
        self.completeState = completeState
        self.completeState.dispatch.enter()
    }
    
    func start() { }
}

And this is the CompleteState Class:

class CompleteState {
    let userId:String
    let dispatch = DispatchGroup()
    
    init(userId:String) {
        self.userId = userId
        dispatch.notify(queue: DispatchQueue.main) {
            print("Finish load all data")
        }
    }
}

And I have multiple classes that inherit from BaseClass, and all of them are in this form:

class ExampleClass1: BaseClass {
    override func start() {
        DispatchQueue.global().async {
            print("ExampleClass1 - Start running")

            //DOING SOME CALCULATIONS
            
            print("ExampleClass1 - Finish running")
            self.completeState.dispatch.leave()
        }
    }
}

This is the code that runs all of this:

public class RunAll {
    let completeState: CompleteState
    let classArray: [BaseClass]
    
    public init(userId: String) {
        self.completeState = CompleteState(userId: userId)
        classArray = [ExampleClass1(completeState: completeState),
                          ExampleClass2(completeState: completeState),
                          ExampleClass3(completeState: completeState),
                          ExampleClass4(completeState: completeState),
                          ExampleClass5(completeState: completeState),
                          ExampleClass6(completeState: completeState),
                          ExampleClass7(completeState: completeState),
                          ExampleClass8(completeState: completeState),
                          ExampleClass9(completeState: completeState)]

        startClasses()
    }
    
    private func startClasses() {
        for foo in classArray {
            foo.start()
        }
    }
}

And the problem is that I get dispatch.notify called before all the classes finish their work, any idea what is the problem?

CodePudding user response:

The documentation for notify states:

This function schedules a notification block to be submitted to the specified queue when all blocks associated with the dispatch group have completed. If the group is empty (no block objects are associated with the dispatch group), the notification block object is submitted immediately. When the notification block is submitted, the group is empty.

You are calling dispatch.notify() in your CompleteState init. At the time you are calling notify, the dispatch group is empty, because you haven't yet created a BaseClass subclass let along called start, which is where the dispatch group is entered.

Because you call notify when the dispatch group is empty, the block is submitted immediately.

It may be worth looking into OperationQueue, but if you want to use what you have you can split the notify out from the init:

lass CompleteState {
    let userId:String
    let dispatch = DispatchGroup()
    
    init(userId:String) {
        self.userId = userId
    }
    
    func registerCompletionBlock(handler: @escaping () -> Void) {
        self.dispatch.notify(queue: DispatchQueue.main, execute: handler)
    }
}

You would then provide the completion block after you call startClasses

public init(userId: String) {
        self.completeState = CompleteState(userId: userId)
        classArray = [ExampleClass1(completeState: completeState),
                          ExampleClass2(completeState: completeState),
                          ExampleClass3(completeState: completeState),
                          ExampleClass4(completeState: completeState),
                          ExampleClass5(completeState: completeState),
                          ExampleClass6(completeState: completeState),
                          ExampleClass7(completeState: completeState),
                          ExampleClass8(completeState: completeState),
                          ExampleClass9(completeState: completeState)]

        startClasses()
        self.completeState.registerCompletionBlockHandler {
            print("Finish load all data")
        }
    }
  • Related