swift semaphore command-line-tool project wait for group finished

273 views Asked by At

seem don't wait the end of the group

let oneSem_1 = DispatchSemaphore(value: 1)
let semaphore = DispatchSemaphore(value: 4)
let semaphoreEND = DispatchSemaphore(value: 0)
var p=0
let group_2 = DispatchGroup()
var t:[Int]=[]
let MAX=1000000
for _ in 0..<MAX {
    group_2.enter()
    DispatchQueue.global().async{
        //group_2.enter()
        semaphore.wait()

        oneSem_1.wait()
        p+=1
        t.append(p)//car ressource critique, sinon pas rempli à fond
        oneSem_1.signal()

        if p == MAX{
            print("p == MAX")
            semaphoreEND.signal()
        }

        semaphore.signal()
        //group_2.leave()
    }
    group_2.leave()
}

group_2.wait()
//    semaphoreEND.wait()

//    while(p != MAX){
//        usleep(1_00_000)
//        print("p=",p)
//    }
print("END   p=\(p)  t.count=\(t.count)")

I expect the output of ND p=1000000 t.count=1000000

I can get this result if i uncomment // semaphoreEND.wait()

, but the actual output is

END p=999871 t.count=999881

Other problem : t.count != p

With Group i expect the end of all task. Why i have to uncomment // semaphoreEND.wait() ?

Thanks

Project here for download : https://github.com/fredOnGitHub/semaphore_to_modify_1

3

There are 3 answers

0
user7058377 On

AGAIN ANOTHER SOLUTION FOR THIS PROBLEM (without waiting group)

using Apple Processes and Threads > RunLoop : : according to the council of vadian

See waiting until the task finishes , Fundation > Processes-And-Threads > RunLoop

// Store a reference to the current run loop
var shouldKeepRunning = true
let runLoop = RunLoop.current
func task(){
    //sleep(1)
    oneSem_1.wait()
    p+=1
    t.append(p)//ressource critique, sinon pas rempli à fond
    if p == MAX{
        print("p == MAX")
//        shouldKeepRunning = false//BUG!!
        DispatchQueue.main.async{//METTRE CECI
            shouldKeepRunning = false
        }
    }
    oneSem_1.signal()
}
for _ in 0..<MAX {
    DispatchQueue.global().async{
        semaphore.wait()
        //print("wake up")
        task()
        //print("end")
        semaphore.signal()
    }
}
// Start run loop after work has been started
print("start")
while shouldKeepRunning  && runLoop.run(mode: .default, before: .distantFuture)
{
    print("WROTE ONLY ONCE")

}
print("END   p=\(p)  t.count=\(t.count)")

REMARQUE:

here Apple say "The RunLoop class is generally not considered to be thread-safe and its methods should only be called within the context of the current thread. You should never try to call the methods of an RunLoop object running in a different thread, as doing so might cause unexpected results."

and give an example. Here the changes are :

  1. using a variable more
  2. have to use DispatchQueue.main.async{ and only that

I would like to know the best between both these another solutions because even if you have more variable it is herited from Threads > RunLoop

0
user7058377 On

SOLUTION FOR THIS PROBLEM:

I have put group_2.leave() in a bad place. Like here Waiting until the task finishes && GCD explain and others like 2. DispatchGroup you have to put it in the async function in his completion

func task(){
//    sleep(1)
    oneSem_1.wait()
    p+=1
    t.append(p)//car ressource critique, sinon pas rempli à fond
    oneSem_1.signal()
}

print("start")
for _ in 0..<MAX {
    group_2.enter()
    DispatchQueue.global().async{
        semaphore.wait()
//        print("wake up")
        task()
//        print("end")
        group_2.leave()
        semaphore.signal()
    }
}
group_2.wait()
print("END   p=\(p)  t.count=\(t.count)")

With this solution i don't need to test if p == MAX to unlock the final print of the array (semaphoreEND.signal())

However there is a good note from vadian "No, you have to start and stop the runloop explicitly."

I look for this implementation, it is amazing, exiting

0
user7058377 On

ANOTHER SOLUTION FOR THIS PROBLEM (without waiting group) : according to the council of vadian

see How to exit a runloop?, Core Fundation > CFRunLoop

let oneSem_1 = DispatchSemaphore(value: 1)
let semaphore = DispatchSemaphore(value: 4)
var p=0
var t:[Int]=[]
let MAX=100_000
#if DEBUG
print("DEBUG")
// Store a reference to the current run loop
let runLoop = CFRunLoopGetCurrent()
#endif

func task(){
    //sleep(1)
    oneSem_1.wait()
    p+=1
    t.append(p)//ressource critique, sinon pas rempli à fond
    if p == MAX{
        #if DEBUG
        print("CFRunLoopStop(runLoop)")
        CFRunLoopStop(runLoop)
        #else
        DispatchQueue.main.async{
            print("CFRunLoopStop(CFRunLoopGetCurrent())")
            CFRunLoopStop(CFRunLoopGetCurrent())
        }
        #endif
    }
    oneSem_1.signal()
}
for _ in 0..<MAX {
    DispatchQueue.global().async{
        semaphore.wait()
//        print("wake up")
        task()
//        print("end")
        semaphore.signal()
    }
}
// Start run loop after work has been started
print("start")
CFRunLoopRun()
print("END   p=\(p)  t.count=\(t.count)")

Remarque:

If you want to use CFRunLoopStop(CFRunLoopGetCurrent()) it is in DispatchQueue.main.async{ else you have to initialize let runLoop = CFRunLoopGetCurrent() and you can directly do CFRunLoopStop(runLoop) and that's not a problem if have created a lot of thread. Maybe it is why Apple says "...CFRRun loops can be run recursively..."