SwiftでArrayに対する処理をglobal queueで行った時の順序について

公開日:2018-11-08
最終更新:2018-11-08

SwiftのDiscordコミュニティで交わされる会話についていけないことがある。実務経験1年弱の自分の今のスキルと大きく乖離があるので自分で調べ直して理解しないといけない現状。 そこでSwiftのDiscordコミュニティで話をされていたことで自分がすぐに理解出来なかったこと且つ興味の湧いた所を時々残していきたいと思う。

環境

Swift 4.2

配列に対するメインスレッドでの並列処理

import Foundation

var array = Array(repeating: 1, count: 100000)

DispatchQueue.global().async {
  array.insert(3, at: 99999)
  print("inserted")
}

print("start")
let total = array.reduce(0, +)
print(total)
print(array.count)

これの実行結果が以下。Command line executable

# 順番は実行するたびに変わる恐れあり
start
inserted
100000
100001

Command line executableの場合のmain()は生のmain。 dispachMain()を自分で呼ぶなどしないとDispachQueueの支配するメインスレッドにならない。のでlet totalの行は指定しているqueueの中で実行されていない。

DispatchQueue.asyncの並列実行は、発火しても、スケジュールされるかどうかは運次第なので、asyncが実行される前にarray.reduceが終わってしまう可能性があります。 https://discordapp.com/channels/291054398077927425/291211035438874625/504148932818108419

これに関しては今まで開発で使ってきたので納得。

global dispatch queueはconcurrent queue。オブジェクトが1つだとしても。QoSについてはEnergy Efficiency Guide for iOS Apps: Prioritize Work with Quality of Service Classesを参照。

Arrayはスレッドセーフなのか

明確にスレッドセーフと書かれているわけではないらしい。デフォルトで非スレッドセーフで特に何も書かれていないから、スレッドセーフではないと考えられる、とのこと。

Collection Types The Swift Programming Language (Swift 4.2)

OwnershipManifestoには

The most important consequence of this is that two different array elements cannot be simultaneously accessed. This will interfere with certain common idioms for working with arrays, although some cases (like concurrently modifying different slices of an array) are already quite problematic in Swift.

[OwnershipManifesto.md#subscripts] (https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#subscripts)

arrayの異なるsubscriptにconcurrentに同時にアクセスするのはNGという言及がされている。Discordでは同時にという表現ではなく、concurrentにという表現だったがドキュメントからconcurrentにとは読み取れなかった。

そもそも自分がスレッドセーフという概念を正確に理解しているのか心配になってきた。

スレッドセーフ(Thread-safe)は、マルチスレッドプログラミングにおける概念である。あるコードがスレッドセーフであるという場合、そのコードを複数のスレッドが同時並行的に実行しても問題が発生しないことを意味する。特に、ある共有データへの複数のスレッドによるアクセスがあるとき、一度に1つのスレッドのみがその共有データにアクセスするようにして安全性を確保しなければならない。

スレッドセーフ - Wikipedia

Thread-Unsafeは任意のオブジェクトが同時に複数のスレッドによって変更を許可する場合。 Thread-Safeは任意のオブジェクトが同時に複数のスレッドで変更を許可しない場合。 Immutableなobjectsは基本スレッドセーフって考えて良さそうだなとか考えてたがAppleの公式ドキュメントがあるのでこっち読んだほうが良い。 納得したのでArrayの話に戻る。といってもスレッドセーフじゃないことを確認するコードを書くだけ。

こんな風に複数のスレッドで変更しようとすると警告が出る

import Foundation

var a = [1,2,3]
DispatchQueue.global().async {
    a.append(5)
}
DispatchQueue.global().async {
    a.append(6)
}
// Fatal error: UnsafeMutablePointer.deinitialize with negative count

別の変数に同期をとると回避出来る。

import Foundation

var a = [1, 2, 3]
var b = a
DispatchQueue.global().async {
    a.append(4)
}

DispatchQueue.global().async {
    b.append(5)
}

Arrayはスレッドセーフじゃないと認識して良さそう。

SwiftではArrayやDictionaryはCopy on Writeになっているがスレッドセーフじゃないのか?

In Swift, Array, String, and Dictionary are all value types. They behave much like a simple int value in C, acting as a unique instance of that data. You don’t need to do anything special such as making an explicit copy to prevent other code from modifying that data behind your back. Importantly, you can safely pass copies of values across threads without synchronization. In the spirit of improving safety, this model will help you write more predictable code in Swift. Value and Reference Types - Swift Blog - Apple Developer

同期をとることなく安全にスレッド間で値のコピーを渡すことが出来るとのこと。CoWという仕組み自体はスレッドセーフと考えてよさそう。

CoWの実装方法は@omochimetaruさんのSwift での Copy on Write の実装方法の解説 - Qiitaという記事がわかりやすかった。

※プログラミングの経験が浅いこともあり誤りや非効率なコードがあるかと思います。何かお気づきの際はコメント等でご指摘お願いします。

記事が少しでもいいなと思ったらクラップを送ってみよう!
36
+1
@nabeatsuの技術ブログ

よく一緒に読まれている記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる

技術ブログをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

技術ブログを開設する

Qrunchでアウトプットをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

Markdownで書ける

ログ機能でアウトプットを加速

デザインのカスタマイズが可能

技術ブログ開設

ここから先はアカウント(ブログ)開設が必要です

英数字4文字以上
.qrunch.io
英数字6文字以上
ログインする