SwiftUIのDispatchQueue main/global() sync/asyncの違いを実験で理解した結果を説明する。
結論
以下8パターンの実験を行った。
- メインスレッドからDispatchQueue.main.sync
- メインスレッドからDispatchQueue.main.async
- メインスレッドからDispatchQueue.global().sync ★1
- メインスレッドからDispatchQueue.global().async
- 外部スレッドからDispatchQueue.main.sync ★2
- 外部スレッドからDispatchQueue.main.async ★2
- 外部スレッドからDispatchQueue.global().sync
- 外部スレッドからDispatchQueue.global().async ★1
★1,★2は以下の特徴があることがわかった。
- ★1 並列処理ができるため処理を高速化できる。
- ★2 メインスレッドに処理を渡すことができる。
実験結果の概略を以下に図で示す。
メインスレッドから実行させた場合
.sync | .async | |
main | エラー発生 | |
global() |
外部スレッドから実行させた場合
.sync | .async | |
main | ||
global() |
以下に実験内容を示す。
実験1 メインスレッドから実行させる
DispatchQueue.main.sync
import SwiftUI
struct ContentView: View {
var body: some View {
Button("ボタンをタップ") {
print("始め。", 今のスレッドを表示())
iを数える()
print("終わり。", 今のスレッドを表示())
}
.buttonStyle(.borderedProminent)
}
func iを数える() {
for i in 0..<9 {
DispatchQueue.main.sync { // ? ここをいろいろ試す。
sleep(1)
print(i, 今のスレッドを表示())
}
}
}
func 今のスレッドを表示() -> String {
return "\(Thread.current)"
}
}
メインスレッドからDispatchQueue.main.syncを実行するとエラー Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) が発生する。
DispatchQueue.main.async
import SwiftUI
struct ContentView: View {
var body: some View {
Button("ボタンをタップ") {
print("始め。", 今のスレッドを表示())
iを数える()
print("終わり。", 今のスレッドを表示())
}
.buttonStyle(.borderedProminent)
}
func iを数える() {
for i in 0..<9 {
DispatchQueue.main.async { // ? ここをいろいろ試す。
sleep(1)
print(i, 今のスレッドを表示())
}
}
}
func 今のスレッドを表示() -> String {
return "\(Thread.current)"
}
}
全てメインスレッド(number = 1)で実行された。
始め。 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
終わり。 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
0 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
1 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
2 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
3 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
4 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
5 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
6 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
7 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
8 <_NSMainThread: 0x600002c4c000>{number = 1, name = main}
DispatchQueue.global().sync
import SwiftUI
struct ContentView: View {
var body: some View {
Button("ボタンをタップ") {
print("始め。", 今のスレッドを表示())
iを数える()
print("終わり。", 今のスレッドを表示())
}
.buttonStyle(.borderedProminent)
}
func iを数える() {
for i in 0..<9 {
DispatchQueue.global().sync { // ? ここをいろいろ試す。
sleep(1)
print(i, 今のスレッドを表示())
}
}
}
func 今のスレッドを表示() -> String {
return "\(Thread.current)"
}
}
DispatchQueue.main.asyncのときと似ているが「終わり。」が最後に実行された。全てメインスレッド(number = 1)で実行された。
始め。 <_NSMainThread: 0x600001600000>{number = 1, name = main}
0 <_NSMainThread: 0x600001600000>{number = 1, name = main}
1 <_NSMainThread: 0x600001600000>{number = 1, name = main}
2 <_NSMainThread: 0x600001600000>{number = 1, name = main}
3 <_NSMainThread: 0x600001600000>{number = 1, name = main}
4 <_NSMainThread: 0x600001600000>{number = 1, name = main}
5 <_NSMainThread: 0x600001600000>{number = 1, name = main}
6 <_NSMainThread: 0x600001600000>{number = 1, name = main}
7 <_NSMainThread: 0x600001600000>{number = 1, name = main}
8 <_NSMainThread: 0x600001600000>{number = 1, name = main}
終わり。 <_NSMainThread: 0x600001600000>{number = 1, name = main}
DispatchQueue.global().async
import SwiftUI
struct ContentView: View {
var body: some View {
Button("ボタンをタップ") {
print("始め。", 今のスレッドを表示())
iを数える()
print("終わり。", 今のスレッドを表示())
}
.buttonStyle(.borderedProminent)
}
func iを数える() {
for i in 0..<9 {
DispatchQueue.global().async { // ? ここをいろいろ試す。
sleep(1)
print(i, 今のスレッドを表示())
}
}
}
func 今のスレッドを表示() -> String {
return "\(Thread.current)"
}
}
メインスレッド(number = 1)以外のスレッド(number = 1以外)が9本立って並列に実行された。
始め。 <_NSMainThread: 0x600003fe02c0>{number = 1, name = main}
終わり。 <_NSMainThread: 0x600003fe02c0>{number = 1, name = main}
5 <NSThread: 0x600003fd8c40>{number = 4, name = (null)}
2 <NSThread: 0x600003fa8f80>{number = 5, name = (null)}
4 <NSThread: 0x600003fe5b40>{number = 3, name = (null)}
0 <NSThread: 0x600003f80080>{number = 7, name = (null)}
6 <NSThread: 0x600003f850c0>{number = 10, name = (null)}
7 <NSThread: 0x600003fb0000>{number = 11, name = (null)}
1 <NSThread: 0x600003fbd240>{number = 6, name = (null)}
8 <NSThread: 0x600003fa3180>{number = 12, name = (null)}
3 <NSThread: 0x600003fa3380>{number = 9, name = (null)}
実験2 外部スレッドから実行させる
DispatchQueue.main.sync
import SwiftUI
struct ContentView: View {
var body: some View {
Button("ボタンをタップ") {
print("始め。", 今のスレッドを表示())
外のスレッドに行く()
print("終わり。", 今のスレッドを表示())
}
.buttonStyle(.borderedProminent)
}
func 外のスレッドに行く() {
DispatchQueue.global().async {
// このDispatchQueueは外のスレッドに行きたいだけ。
print("外のスレッド始め。", 今のスレッドを表示())
iを数える()
print("外のスレッド終わり。", 今のスレッドを表示())
}
}
func iを数える() {
for i in 0..<9 {
DispatchQueue.main.sync { // ? ここをいろいろ試す。
sleep(1)
print(i, 今のスレッドを表示())
}
}
}
func 今のスレッドを表示() -> String {
return "\(Thread.current)"
}
}
全てメインスレッド(number = 1)で実行された。syncなので外のスレッドとも同期していて「外のスレッド始め。」と「外のスレッド終わり。」の間に実行されている。
始め。 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
終わり。 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
外のスレッド始め。 <NSThread: 0x600000908800>{number = 6, name = (null)}
0 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
1 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
2 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
3 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
4 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
5 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
6 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
7 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
8 <_NSMainThread: 0x6000009482c0>{number = 1, name = main}
外のスレッド終わり。 <NSThread: 0x600000908800>{number = 6, name = (null)}
DispatchQueue.main.async
import SwiftUI
struct ContentView: View {
var body: some View {
Button("ボタンをタップ") {
print("始め。", 今のスレッドを表示())
外のスレッドに行く()
print("終わり。", 今のスレッドを表示())
}
.buttonStyle(.borderedProminent)
}
func 外のスレッドに行く() {
DispatchQueue.global().async {
// このDispatchQueueは外のスレッドに行きたいだけ。
print("外のスレッド始め。", 今のスレッドを表示())
iを数える()
print("外のスレッド終わり。", 今のスレッドを表示())
}
}
func iを数える() {
for i in 0..<9 {
DispatchQueue.main.async { // ? ここをいろいろ試す。
sleep(1)
print(i, 今のスレッドを表示())
}
}
}
func 今のスレッドを表示() -> String {
return "\(Thread.current)"
}
}
全てメインスレッド(number = 1)で実行された。asyncなので外のスレッドとは非同期で「外のスレッド終わり。」が先に実行されている。
始め。 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
終わり。 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
外のスレッド始め。 <NSThread: 0x600001ddc180>{number = 6, name = (null)}
外のスレッド終わり。 <NSThread: 0x600001ddc180>{number = 6, name = (null)}
0 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
1 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
2 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
3 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
4 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
5 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
6 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
7 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
8 <_NSMainThread: 0x600001db4100>{number = 1, name = main}
DispatchQueue.global().sync
import SwiftUI
struct ContentView: View {
var body: some View {
Button("ボタンをタップ") {
print("始め。", 今のスレッドを表示())
外のスレッドに行く()
print("終わり。", 今のスレッドを表示())
}
.buttonStyle(.borderedProminent)
}
func 外のスレッドに行く() {
DispatchQueue.global().async {
// このDispatchQueueは外のスレッドに行きたいだけ。
print("外のスレッド始め。", 今のスレッドを表示())
iを数える()
print("外のスレッド終わり。", 今のスレッドを表示())
}
}
func iを数える() {
for i in 0..<9 {
DispatchQueue.global().sync { // ? ここをいろいろ試す。
sleep(1)
print(i, 今のスレッドを表示())
}
}
}
func 今のスレッドを表示() -> String {
return "\(Thread.current)"
}
}
全て外部スレッド(number = 6)で実行された。syncなので外のスレッドに同期していて「外のスレッド始め。」と「外のスレッド終わり。」の間に実行されている。
始め。 <_NSMainThread: 0x600003810000>{number = 1, name = main}
終わり。 <_NSMainThread: 0x600003810000>{number = 1, name = main}
外のスレッド始め。 <NSThread: 0x600003850a40>{number = 6, name = (null)}
0 <NSThread: 0x600003850a40>{number = 6, name = (null)}
1 <NSThread: 0x600003850a40>{number = 6, name = (null)}
2 <NSThread: 0x600003850a40>{number = 6, name = (null)}
3 <NSThread: 0x600003850a40>{number = 6, name = (null)}
4 <NSThread: 0x600003850a40>{number = 6, name = (null)}
5 <NSThread: 0x600003850a40>{number = 6, name = (null)}
6 <NSThread: 0x600003850a40>{number = 6, name = (null)}
7 <NSThread: 0x600003850a40>{number = 6, name = (null)}
8 <NSThread: 0x600003850a40>{number = 6, name = (null)}
外のスレッド終わり。 <NSThread: 0x600003850a40>{number = 6, name = (null)}
DispatchQueue.global().async
import SwiftUI
struct ContentView: View {
var body: some View {
Button("ボタンをタップ") {
print("始め。", 今のスレッドを表示())
外のスレッドに行く()
print("終わり。", 今のスレッドを表示())
}
.buttonStyle(.borderedProminent)
}
func 外のスレッドに行く() {
DispatchQueue.global().async {
// このDispatchQueueは外のスレッドに行きたいだけ。
print("外のスレッド始め。", 今のスレッドを表示())
iを数える()
print("外のスレッド終わり。", 今のスレッドを表示())
}
}
func iを数える() {
for i in 0..<9 {
DispatchQueue.global().async { // ? ここをいろいろ試す。
sleep(1)
print(i, 今のスレッドを表示())
}
}
}
func 今のスレッドを表示() -> String {
return "\(Thread.current)"
}
}
別のスレッドが9本立って並列に実行された。
始め。 <_NSMainThread: 0x60000079c540>{number = 1, name = main}
終わり。 <_NSMainThread: 0x60000079c540>{number = 1, name = main}
外のスレッド始め。 <NSThread: 0x6000007c0000>{number = 6, name = (null)}
外のスレッド終わり。 <NSThread: 0x6000007c0000>{number = 6, name = (null)}
5 <NSThread: 0x6000007d1100>{number = 5, name = (null)}
0 <NSThread: 0x6000007f8200>{number = 7, name = (null)}
2 <NSThread: 0x6000007f5d80>{number = 9, name = (null)}
1 <NSThread: 0x600000796a00>{number = 4, name = (null)}
3 <NSThread: 0x6000007d93c0>{number = 3, name = (null)}
4 <NSThread: 0x6000007c0000>{number = 6, name = (null)}
7 <NSThread: 0x6000007faf00>{number = 11, name = (null)}
8 <NSThread: 0x6000007e8600>{number = 12, name = (null)}
6 <NSThread: 0x6000007c8040>{number = 10, name = (null)}
まとめ
SwiftUIのDispatchQueue main/global() sync/asyncの違いを実験で理解した結果を説明した。
コメント