GCD (Grand Central Dispatch)はmacOSやiOSのマルチコア環境で、 効率的に並列処理を実行するための仕組み。 OperationQueueというのもあるが、これもGCD上で動く。
DispatchQueue
処理をどのスレッドで実行するか管理するキュー。 どこからでも参照できるmainとglobalのキュー以外に新しくキューを作成することもできる。 labelは衝突しないようにreverse-DNS nameにすることが推奨されている。
DispatchQueue.main.async {}
DispatchQueue.global(qos: .default).async {}
DispatchQueue.global(qos: .background).async {}
DispatchQueue(label: "net.sambaiz.serial_dispatch_queue").async {}
DispatchQueue(label: "net.sambaiz.concurrent_dispatch_queue", attributes: .concurrent).async {}
sync/async
ブロッキングするsync()
としないasync()
。排他制御ではないのに注意。
DispatchQueue.global().async {
print("async")
DispatchQueue.main.sync { print("sync") }
print("done")
}
print("run")
run
async
sync
done
serial/concurrent
処理を単一のスレッドで行う(serial)か、複数のスレッドで行う(concurrent)かはキューによって決まり、
メインスレッドで動かすmainはserial、globalはconcurrentになっている。
自作のキューの場合は作成時に attributes: .concurrent
を渡すとconcurrentになり、渡さないとserialになる。
まずはconcurrentの例から。
for i in 1...3 {
DispatchQueue.global().async {
print("start concurrent \(i) thread: \(Thread.current)")
print("return concurrent \(i) thread: \(Thread.current)")
}
}
print("return thread: \(Thread.current)")
別スレッドで並列に動いている。
start concurrent 3 thread: <NSThread: 0x600001646440>{number = 5, name = (null)}
start concurrent 2 thread: <NSThread: 0x600001631600>{number = 6, name = (null)}
return thread: <NSThread: 0x60000160a8c0>{number = 1, name = main}
start concurrent 1 thread: <NSThread: 0x600001640b80>{number = 7, name = (null)}
return concurrent 1 thread: <NSThread: 0x600001640b80>{number = 7, name = (null)}
return concurrent 3 thread: <NSThread: 0x600001646440>{number = 5, name = (null)}
return concurrent 2 thread: <NSThread: 0x600001631600>{number = 6, name = (null)}
次にserialの例。
for i in 1...3 {
DispatchQueue.global().async {
print("start concurrent \(i) thread: \(Thread.current)")
DispatchQueue.main.sync {
print(" start serial \(i) thread: \(Thread.current)")
print(" return serial \(i) thread: \(Thread.current)")
}
print("return concurrent \(i) thread: \(Thread.current)")
}
}
シングルスレッドで、ある処理が実行されている間は他の処理が走っていない。 古くからあるNSLockでも排他制御することはできるが、 コストが高い。
start concurrent 1 thread: <NSThread: 0x600001c0c200>{number = 5, name = (null)}
start concurrent 3 thread: <NSThread: 0x600001c60080>{number = 6, name = (null)}
start concurrent 2 thread: <NSThread: 0x600001c26200>{number = 4, name = (null)}
start serial 1 thread: <NSThread: 0x600001c2a8c0>{number = 1, name = main}
return serial 1 thread: <NSThread: 0x600001c2a8c0>{number = 1, name = main}
start serial 2 thread: <NSThread: 0x600001c2a8c0>{number = 1, name = main}
return concurrent 1 thread: <NSThread: 0x600001c0c200>{number = 5, name = (null)}
return serial 2 thread: <NSThread: 0x600001c2a8c0>{number = 1, name = main}
start serial 3 thread: <NSThread: 0x600001c2a8c0>{number = 1, name = main}
return concurrent 2 thread: <NSThread: 0x600001c26200>{number = 4, name = (null)}
return serial 3 thread: <NSThread: 0x600001c2a8c0>{number = 1, name = main}
return concurrent 3 thread: <NSThread: 0x600001c60080>{number = 6, name = (null)}
参考
iOSTraining/1-2_Grand-Central-Dispatch.md at master · mixi-inc/iOSTraining