проект инструмента командной строки быстрого семафора дождитесь завершения группы
Кажется , не ждите конца группы
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, работающего в другом потоке, поскольку это может привести к неожиданным результатам ".
и приведу пример. Вот изменения:
- используя переменную больше
- необходимо использовать 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 можно запускать рекурсивно..."