проект инструмента командной строки быстрого семафора дождитесь завершения группы

Кажется , не ждите конца группы

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)")

Ожидаю на выходе ND p=1000000 t.count=1000000

Я могу получить этот результат, если раскомментирую // семафор END.wait ()

, но фактический результат

КОНЕЦ p = 999871 t.count = 999881

Другая проблема: t.count! = P

С Group я ожидаю конца всей задачи. Почему мне нужно раскомментировать // семафор END.wait()?

Спасибо

Проект здесь для загрузки: https://github.com/fredOnGitHub/semaphore_to_modify_1

3 ответа

ЕЩЕ ОДИН РЕШЕНИЕ ЭТОЙ ПРОБЛЕМЫ (без группы ожидания)

с использованием процессов и потоков Apple> RunLoop:: согласно совету vadian

Смотрите ожидание завершения задачи, 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)")

ЗАМЕЧАНИЕ:

здесь Apple заявляет: "Класс RunLoop обычно не считается потокобезопасным, и его методы должны вызываться только в контексте текущего потока. Вы никогда не должны пытаться вызвать методы объекта RunLoop, работающего в другом потоке, поскольку это может привести к неожиданным результатам ".

и приведу пример. Вот изменения:

  1. используя переменную больше
  2. необходимо использовать DispatchQueue.main.async { и только это

Я хотел бы знать лучшее между обоими этими другими решениями, потому что даже если у вас больше переменных, они наследуются от Threads> RunLoop

РЕШЕНИЕ ЭТОЙ ПРОБЛЕМЫ:

Я поставил group_2.leave() в плохое место. Как здесь Ожидание завершения задачи && GCD объяснение и другие подобные 2. DispatchGroup, вы должны поместить его в функцию async в его завершении

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)")

С этим решением мне не нужно проверять, если p == MAX, чтобы разблокировать окончательную печать массива (semaphoreEND.signal())

Однако есть хорошая заметка от vadian: "Нет, вы должны запускать и останавливать цикл выполнения явно".

Я ищу эту реализацию, она потрясающая, выходящая

ДРУГОЕ РЕШЕНИЕ ЭТОЙ ПРОБЛЕМЫ (без группы ожидания): согласно совету vadian

см. Как выйти из цикла выполнения?, Основное финансирование> 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)")

Ремарк:

Если вы хотите использовать CFRunLoopStop(CFRunLoopGetCurrent()), он находится в DispatchQueue.main.async{иначе вам нужно инициализировать let runLoop = CFRunLoopGetCurrent(), и вы можете напрямую выполнить CFRunLoopStop(runLoop), и это не проблема, если вы создали много нити. Возможно, именно поэтому Apple говорит: "... Циклы CFRRun можно запускать рекурсивно..."

Другие вопросы по тегам