したがって、アプリケーションがあり、
main thread
で実行されます。
main thread
は、ユーザーインターフェイス(
UI
)を表示するコードを実行します。 ネットワークからデータをダウンロードしたり、
main thread
(メインスレッド)で画像を処理するなど、アプリケーションに「時間のかかる」コードを追加し始めるとすぐに、
UI
の動作が大幅に遅くなり、完全な「フリーズ」に至ることさえあります。
![](https://habrastorage.org/files/d6b/e2f/372/d6be2f372083452f922723c9c934e811.png)
このような問題が発生しないように、アプリケーションのアーキテクチャを変更するにはどうすればよいですか? この場合、マルチスレッド(
oncurrency
)が助けとなり、2つ以上の独立したタスク(
tasks
)を同時に実行できます:計算、ネットワークまたはディスクからのデータのダウンロード、画像処理など。
任意の時点でプロセッサがタスクの1つを実行でき、対応するスレッドがそれに割り当てられます。
シングルコアプロセッサ(iPhoneおよびiPad)の場合、タスクが実行される「スレッド」間の複数の短期スイッチによってマルチスレッド(
oncurrency
)が実現され、シングルコアプロセッサでのタスクの同時実行の信頼性の高いアイデアが作成されます。 マルチコアプロセッサ(Mac)では、タスクに関連付けられた各「スレッド」に、タスクを実行するための独自のカーネルが提供されるという事実によって、マルチスレッドが実現されます。 これらの技術はどちらも、マルチスレッド(
oncurrency
)の一般的な概念を使用しています。
アプリケーションにマルチスレッドを導入することに対する一種の支払いは、さまざまなスレッドで安全なコード実行を確保することが難しいことです(
thread safety
)。 タスク(
tasks
)を並行して動作させるとすぐに、異なるタスク(
tasks
)が同じリソースにアクセスしたい、たとえば異なるスレッドの同じ変数を変更したい、または取得したいという事実に関連する問題があります他のタスクによってすでにブロックされているリソースへのアクセス。 これにより、他のスレッドのタスクによって使用されているリソース自体が破壊される可能性があります。
iOSプログラミングでは、 スレッド 、 Grand Central Dispatch (略してGCD)、および操作という複数のツールの形式で開発者にマルチスレッドが提供され、ユーザーインターフェイスの生産性と応答性を向上させるために使用されます。 これは低レベルのメカニズムであるため、
Thread
は考慮しませんが、この記事では
GCD
に焦点を当て、将来の出版物では
Operation
(
GCD
上に構築されたオブジェクト指向
API
)に焦点を当て
API
。
Swift 3
が登場する前は、
Grand Central Dispatch (GCD
)などの強力なフレームワークにはCベースの
API
がありましたが、これは一見魔法のように思えます。タスク。
Swift 3
すべてが劇的に変化しました。
GCD
は、非常に使いやすい新しい完全に
Swift
構文を受け取りました。 古い
API GCD
に少しでも精通していれば、まったく新しい構文は簡単に理解できます。 そうでない場合は、別の通常の
iOS
プログラミングセクションを勉強する必要があります。 新しい
GCD
フレームワークは、すべての
iOS
デバイスを含む
Apple Watch
から
Apple TV
および
Mac
まで、すべての
Apple
デバイスの
Swift 3
で動作します。
もう1つの良いニュースは、
Xcode 8
以降では、
Playgroud
などの強力で視覚的なツールを使用して
GCD
と
Operation
を学習できることです。
Xcode 8
は
PlaygroudPage
と呼ばれる新しいヘルパークラスを導入します。これは、
Playgroud
を無期限に
Playgroud
させる機能を備えています。 この場合、
DispatchQueue
キューは、ジョブが完了するまで実行できます。 これは、ネットワーク要求にとって特に重要です。
PlaygroudPage
クラスを使用するには、
PlaygroudSupport
モジュールをインポートする必要があります。 このモジュールを使用すると、
run loop
にアクセスし、ライブ
UI
表示し、
Playgroud
非同期操作を実行することもできます。 以下に、この設定が実際にどのように見えるかを示します。
Xcode 8
この新しい
Playground
機能により、
Swift 3
マルチスレッド化について非常に簡単かつ直感的に学習できます。
concurrency
よりよく理解するために、
Apple
は両方のツールが
GCD
と
Operation
動作する抽象的な概念をいくつか導入しました。 基本的な概念は
queue
です。 したがって、
iOS
アプリケーションの開発者の観点から
iOS
マルチスレッド化について話すときは、キューについて話します。 キューは、たとえば映画館へのチケットなど、人々が購入するために並んでいる通常のキューですが、この場合、クロージャーが並んでいます(
closure
-コードの匿名ブロック)。 システムは単にキューに従ってそれらを実行し、次のキューを順番に「引き出し」て、このキューに対応するスレッドで実行するように開始します。 キューは
FIFO
パターン(
FIFO
従います。つまり、最初にキューに入れられた人が最初に実行されます。 多くのキューを持つことができ、システムは各キューの1つをクロージャーに「引き出し」、それらを開始して独自のスレッドで実行します。 これにより、マルチスレッド化が実現します。
ただし、これは
iOS
oncurrency
がどのように機能するかの一般的な考え方にすぎません。 陰謀は、これらのキューが相互に関連するタスクを完了するという意味で(シーケンシャルまたはパラレル)、これらのタスクがキューに配置される機能(同期または非同期)であり、それによって現在のキューをブロックするかしないかです。
シリアル( serial
)およびパラレル( concurrent
)キュー
キューの先頭にあるタスク(クロージャ)がiOSによって「プル」され、完了するまで実行され、次のアイテムがキューからプルされる場合、キューは「
serial
」になります。 これは、
serial queue
またはシリアルキューです。 システムは、キューの先頭にあるクロージャーを「プル」し、特定のスレッドで実行を開始すると、キューを「c
oncurrent
」(マルチスレッド)にできます。 システムにまだリソースがある場合、キューから次の要素を取得し、最初の関数がまだ機能している間に別のスレッドで実行するためにそれを開始します。 そして、システムは多くの機能を拡張できます。 マルチスレッドの一般的な概念と
"concurrent queues"
(マルチスレッドキュー)を混同しないように、
"concurrent queue"
並列キューと呼び
"concurrent queue"
。この並列処理の技術的な実装に入らずに、相互に関連するタスクの順序を参照します。
![](https://habrastorage.org/files/cba/064/d50/cba064d50117484a92217b48d556cafd.png)
serial
(シーケンシャル)キューでは、クロージャーは実行された順序で厳密に完了しますが、
concurrent
(パラレル)キューでは、ジョブは予測不能な方法で終了します。 さらに、
serial
キュー上の特定のタスクグループの合計実行時間が、
concurrent
キュー上の同じタスクグループの実行時間を大幅に超えていることがわかります。 現在の
serial
(シーケンシャル)キューでは、1つのタスクのみが実行され、
concurrent
(パラレル)キューでは、現在の時間のタスク数が変更できます。
同期および非同期のタスク実行
queue
作成されると、
async
現在のキューに関連する同期実行と
async
現在のキューに関連する非同期実行の2つの関数を使用してタスクを
queue
に配置できます。
同期
sync
関数は、タスクが完了した後にのみ制御を現在のキューに返し、現在のキューをブロックします。
![](https://habrastorage.org/files/49f/da3/3d3/49fda33d32be4a6ebb511bf0530a8854.png)
async
機能は、
sync
機能とは対照的に、別のキューでジョブを開始した直後に、完了するのを待たずに、現在のキューに制御を戻します。 したがって、
async
機能は、現在のキューでのタスクの実行をブロックしません。
![](https://habrastorage.org/files/c32/c59/7b6/c32c597b60d24ae69f5fffe4ca155d9c.png)
非同期実行の場合、シーケンシャル(
serial
)キューとしての「別のキュー」になることがあります。
![](https://habrastorage.org/files/cfc/0fb/700/cfc0fb700a034acbab2cc25fc6e14f17.png)
および並列(
concurrent
)キュー:
![](https://habrastorage.org/files/391/9fb/1ff/3919fb1ff81747dca78feabb45e3a9de.png)
開発者のタスクは、
sync
機能を使用して
sync
的にまたは非同期機能を使用して
async
的にキューを選択し、タスク(通常はクロージャー)をこのキューに追加することのみです。その後、
iOS
のみが機能します。
この投稿の最初に示したタスクに戻り、「ネットワークからのデータ」ネットワークから別のキューにデータを受信するタスクの実行を切り替えます。
![](https://habrastorage.org/files/07b/a52/95c/07ba5295c1b540fb8fa2c0bb3d8da835.png)
別の
Dispatch Queue
ネットワークから
Data
を受信した後、
Main thread
送り返し
Main thread
。
![](https://habrastorage.org/files/d3e/85e/84a/d3e85e84a6034f58aa0b4ae8d977d3c6.png)
別の
DispatchQueue
キューでネットワークから
Data
を受信すると、
Main thread
は解放され、
UI
で発生するすべてのイベントを処理します。 この場合の実際のコードがどのように見えるか見てみましょう:
![](https://habrastorage.org/files/3c2/63f/1c5/3c263f1c570a4c35a4d22e56dbabf264.png)
かなりの時間がかかり、
Main queue
をブロックする可能性があるimageURL
imageURL
を介してデータをダウンロードするには、このリソース集中型タスクを、
qos
が
.utility
等しいグローバル並列キューに
.utility
(詳細は後述)。
let imageURL: URL = URL(string: "http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg")! let queue = DispatchQueue.global(qos: .utility) queue.async{ if let data = try? Data(contentsOf: imageURL){ DispatchQueue.main.async { image.image = UIImage(data: data) print("Show image data") } print("Did download image data") } }
データ
data
を受信した後
data
再び
Main queue
に戻り、このデータを
image1.image
して
UI
要素
image1.image
を更新します。
Main queue
から「コストのかかる」タスクの実行を「迂回」して、再びそれに戻るために、別のキューへの切り替えのチェーンを実行することがいかに簡単であるかがわかります。 コードはGithubのEnvironmentPlayground.playgroundにあります。
Main queue
から別のスレッドへの高価なジョブの切り替えは常に非同期であることに注意してください。
「現在のスレッド」は、ジョブが別のキューで終了するのを待機するように強制されるため、キューの
sync
方法には非常に注意する必要があります。
Main queue
で
sync
メソッドを呼び出さないでください。アプリケーションの
deadlock
つながるためです。 (これについては以下で詳しく説明します)
グローバルキュー
特別に作成する必要があるユーザーキューに加えて、
iOS
システムは、
out-of-the-box
グローバルキューを開発者に提供します。 彼らの5:
1.)シーケンシャルキュー
Main queue
。ユーザーインターフェイス(
UI
)を使用したすべての操作が行われます。
let main = DispatchQueue.main
ユーザーインターフェイス(
UI
)、
UIButton
または
UI--
UIButton
何かを行う関数またはクロージャーを実行する場合は、その関数またはクロージャーを
Main queue
配置する必要があり
Main queue
。 このキューは、グローバルキューの中で最も高い優先度を持っています。
2.)
qos
品質が異なり、優先度が異なる4つのバックグラウンド
concurrent
(並列)グローバルキュー:
// let userInteractiveQueue = DispatchQueue.global(qos: .userInteractive) let userInitiatedQueue = DispatchQueue.global(qos: .userInitiated) let utilityQueue = DispatchQueue.global(qos: .utility) // let backgroundQueue = DispatchQueue.global(.background) // let defaultQueue = DispatchQueue.global()
これらの各キュー
Apple
、
Apple
抽象的な「サービス品質」
qos
(
Quality of Service
略)を授与され
Apple
、タスクに対して何をすべきかを決定する必要があります。
さまざまな
qos
を
qos
、それぞれの目的を説明し
qos
。
-
.userInteractive
現在ユーザーとやり取りしているタスクで、ほとんど時間が.userInteractive
タスクの場合:アニメーションは即座に実行されます。 ユーザーはMain queue
でこれを行いたくありませんが、これはできるだけ早く行う必要があります。ユーザーは現在、私とやり取りしているからです。 ユーザーが画面上で指をスワイプし、集中的な画像処理に関連する何かを計算する必要があり、このキューに計算を配置する状況を想像できます。 ユーザーは画面上で指をスライドさせ続けますが、すぐに結果が表示されるわけではありません。計算は時間がかかるため、結果は画面上の指の位置よりもわずかに遅れますが、少なくともMain queue
まだ指を「聞いて」反応します。 このキューの優先度は非常に高くなっていますが、Main queue
よりも低くなっていMain queue
。 -
.userInitiated
ユーザーによって開始され、フィードバックが必要なタスクの場合、これは対話型イベントの内部ではなく、ユーザーは対話を続けるためのフィードバックを待っています。 数秒かかる場合があります。 優先度は高いが、前のキューよりも低い -
.utulity
データのロードやデータベースのクリアなど、完了するまでにある程度の時間を必要とし、即時のフィードバックを必要としないタスクの場合。 ユーザーが要求していないことが行われていますが、このアプリケーションには必要です。 タスクには数秒から数分かかる場合があります。 優先度は前のキューよりも低く、 -
.background
視覚化に関連せず、実行時間に重要ではないタスク。 たとえば、backups
またはweb
サービスとの同期。 これは通常、バックグラウンドで実行されるもので、誰もメンテナンスを必要としない場合にのみ発生します。 ほんの数分から数時間かかるかなりのバックグラウンドタスク。 すべてのグローバルキューの中で最も低い優先度を持ちます。
また、デフォルトの
.default
concurrency
キュー
.default
があります。これは、
qos
「サービス品質」
qos
不足を報告し
qos
。 演算子を使用して作成されます:
DispatchQueue.global()
他のソースから
qos
情報を判別できる場合はそれが使用され、そうでない場合は
.userInitiated
と
.utility
間で
qos
使用されます。
これらのグローバルキューはすべてSYSTEMグローバルキューであり、このキュー内のタスクは私たちのタスクだけではないことを理解することが重要です。 また、1つを除くすべてのグローバルキューが
concurrent
(並列)キューであることを知ることも重要です。
ユーザーインターフェイス用の特別なグローバルシリアルキュー-メインキュー
Appleは、唯一のグローバル
serial
(シーケンシャル)キューを提供しています-これは、上記の
Main queue
です。 このキューでは、
UI
変更に関連しないリソース集約的な操作(たとえば、ネットワークからのデータのダウンロード)を実行して、この操作の間
UI
を「フリーズ」せず、ユーザーインターフェイスをいつでもユーザーの操作に応答させないようにすることは望ましくありません。ジェスチャーで。
![](https://habrastorage.org/files/947/064/a21/947064a217b2454a8b1b629eaad4057c.png)
このようなリソース集中型の操作を他のスレッドまたはキューに「流用」することを強くお勧めします。
![](https://habrastorage.org/files/253/e8a/99b/253e8a99b42c46aba8c17af901618fc7.png)
もう1つ厳しい要件があります。
Main queue
のみ、
UI
要素を変更できます。
これは、
Main queue
UI
アクションに「応答する」だけでなく(はい、これが主な理由)、ユーザーインターフェースをマルチスレッド環境での「混乱」から保護する、つまり、ユーザーアクションは、厳密に順番に実行されます。
UI
要素が異なるキューでアクションを実行できるようにすると、描画が異なる速度で発生し、アクションが交差し、画面上で完全に予測不能になる可能性があります。
Main queue
を一種の「同期ポイント」として使用し、画面に「描画」したいすべてのユーザーが戻ります。
マルチスレッドの問題
タスクの並列動作を許可するとすぐに、異なるタスクが同じリソースにアクセスしたいという事実に関連する問題があります。
主に3つの問題があります。
-
race condition
-システムまたはアプリケーションの動作がコードの一部が実行される順序に依存する、マルチスレッドシステムまたはアプリケーションの設計におけるエラー -
priority inversion
-
deadlock
-複数のスレッドが、これらのスレッド自体が占有するリソースを無限に待機しているマルチスレッドシステムの状況
競合状態
private
キューで
value
変数を非同期に変更し、現在のスレッドで
value
を表示すると、
race condition
最も単純なケースを再現できます。
![](https://habrastorage.org/files/625/51a/fc7/62551afc7c57436eaeab7a4a67dfc9b2.png)
通常の変数
value
とそれを変更する通常の関数
changeValue
があり、意図的に
sleep(1)
演算子を使用して、変数の変更にかなりの時間がかかるようにしました。
async
を使用して
changeValue
関数を
changeValue
実行すると、変更された
value
変数に配置する前に、現在のスレッドで
value
変数を別の値にリセットできます。これが
race condition
です。 このコードは、次の形式での印刷に対応しています。
![](https://habrastorage.org/files/588/996/d03/588996d0319646c4ad766285485fa0ea.png)
「
race condition
」と呼ばれる現象を明確に示す図:
![](https://habrastorage.org/files/4e9/ddb/5de/4e9ddb5deb494edabfc975da717f7958.png)
async
メソッドを
sync
置き換えましょう:
![](https://habrastorage.org/files/75e/608/5ea/75e6085ea469425395705e93e0bf3bf6.png)
印刷と結果の両方が変更されました:
![](https://habrastorage.org/files/940/871/63a/94087163acb44a83906be79f57ba44fb.png)
そして、「
race condition
」と呼ばれる現象がない図:
![](https://habrastorage.org/files/cfe/487/2d8/cfe4872d84724dc6a5d6dea169957456.png)
キューの
sync
メソッドには非常に注意する必要がありますが、「現在のスレッド」はジョブが他のキューで終了するまで待機するため、
sync
メソッドは競合状態を避けるために非常に便利です。
race condition
現象をシミュレートするコードは、GithubのfirstPlayground.playgroundで表示できます。 後で、異なるストリームで受信した文字列を形成するときの実際の「
race condition
」を示します。 「バリア」を使用してストリングを形成するエレガントな方法も提案されます。これにより、「
race conditions
」を回避し、生成されたストリングをスレッドセーフにすることができます。
優先順位の逆転
の概念は、リソースのロックに密接に関連しています。
![](https://habrastorage.org/files/2a4/081/574/2a40815749274c3897e581524cc2f539.png)
優先度が低(A)と高(B)の2つのタスクがシステムにあるとします。時間T1で、タスク(A)はリソースをブロックし、リソースの提供を開始します。時間T2で、タスク(B)は優先度の低いタスク(A)を押し出し、時間T3でリソースを引き継ぎます。ただし、リソースがロックされているため、タスク(B)は保留され、タスク(A)は実行を継続します。時間T4で、タスク(A)はリソースのサービスを完了し、ロックを解除します。タスク(B)はリソースを予期しているため、すぐに実行を開始します。
タイムスパン(T4-T3)は、限定優先順位反転と呼ばれます。この期間には、計画ルールと論理的な矛盾があります。優先度の低いタスクが実行されている間、優先度の高いタスクが待機しています。
しかし、これは最悪ではありません。システムで3つのタスクが機能するとします:低優先度(A)、中優先度(B)、高優先度(C):
![](https://habrastorage.org/files/011/044/6fb/0110446fbda54b8aa59534af9b0a8f78.png)
リソースがタスク(A)によってブロックされ、タスク(C)によって必要とされる場合、同じ状況が観察されます-高優先度タスクがブロックされますしかし、タスク(B)がリソースを待機する(C)後(A)に取って代わったとします。タスク(B)は競合について何も知らないので、一定期間(T5-T4)にわたって好きなだけ実行できます。さらに、(B)に加えて、システムには他のタスクがあり、優先度は(A)よりも大きく(B)低くなります。したがって、期間(T6-T3)の期間は一般に無期限です。この状況は、無制限の優先順位反転と呼ばれます。
一般に、優先度の制限の反転は避けることはできませんが、マルチスレッドアプリケーションにとって無制限のものほど危険ではありません。優先度の低いすべての「干渉」タスクの優先度を強制的に上げることで排除されます。
以下に、
DispatchWorkItem
オブジェクトを使用して現在のキュー内の個々のタスクの優先度を上げる方法を示します。
デッドロック
デッドロックは、リソースロックがネストされているときに発生する可能性があるシステムの緊急状態です。システムに2つのリソース(XおよびY)を使用する低(A)および高(B)優先度の2つの
![](https://habrastorage.org/files/9d8/3a9/844/9d83a98445554b0486486c287d07b16d.png)
タスクがあるとします。時間T1で、タスク(A)はリソースXをブロックします。時刻T3でリソースYをブロックする優先タスク(B)。タスク(B)がリソースYを解放せずにリソースX(T4)をブロックしようとすると、保留され、タスク(A)が続行されます。時間T5で、タスク(A)がXを解放せずにリソースYをブロックしようとすると、相互ロックの状態が発生します。タスク(A)と(B)のいずれも制御を取得できません。
相互ロックは、システムがリソースへの依存(ネスト)マルチスレッドアクセスを使用する場合にのみ可能です。ネストが使用されていない場合、またはリソースが優先度増加プロトコルを使用している場合、相互ブロッキングを回避できます。
投稿の冒頭にあるタスクで、バックグラウンドキューでネットワークからデータを受信した後、メインキューに戻るためにsyncメソッドを使用しようとすると、デッドロック(
deadock
)が発生します。アプリケーションでデッドロック()が発生するため
、でメソッド
sync
を呼び出さ
main queue
ないで
deadlock
ください!
実験環境
実験のために、我々は、使用する
Playground
モジュールC無限の時間に合わせて調整
PlaygroundSupport
し、クラス
PlaygroudPage
我々はキューに置かれたすべてのタスクを完了し、へのアクセスを得ることができたこと
main queue
。
Playground
cコマンドで何らかのイベントを待つのを止めることができ
PlaygroundPage.current.finishExecution()
ます。
別のクールな機能があります
Playground
-
UI
チームの助けを借りて「生きている」と対話する機能
PlaygroundPage.liveView = viewController
およびアシスタントエディター(
Assistant Editor
)。たとえば、を作成する場合、
viewController
を確認するには、コードを無制限に実行
viewController
するように構成
Playground
し、アシスタントエディターを有効にするだけです(
Assistant Editor
)。コマンドをコメント化して、手動で
PlaygroundPage.current.finishExecution()
停止する
Playground
必要があります。
![](https://habrastorage.org/files/4df/034/dfc/4df034dfc92c49aeafe1b0865d2ee172.png)
Playground
実験環境テンプレートのcコードは、EnvironmentPlayground.playgroundという名前で、Githubにあります。
1.最初の実験。グローバルキューとジョブ
簡単な実験から始めましょう。一貫性のある1:我々はまた、世界的なキューの数を定義する
mainQueue
ことがある-
main queue
、そして4つの並列(
concurrent
)
queues
- 、、
userInteractiveQueue
と。デフォルトで設定できます- :ジョブとして、10個の同一の文字と現在のキューの優先度を印刷することを選択します。1文字を印刷する別のタスク、高い優先度で実行します。
userQueue
utilityQueue
backgroundQueue
concurrent queue
defautQueue
![](https://habrastorage.org/files/7c2/c04/586/7c2c0458682a4b6195365c69f9abae0f.png)
task
taskHIGH
![](https://habrastorage.org/files/eeb/a88/223/eeba8822320940a4b55ae0547d9cdcdb.png)
2.第二の実験は、に適用されます同期および非同期的にグローバルキューに
たとえば、グローバルキューを受け取ったら、methodを使用してSYNCHRONOUSLYに、またはmethodを使用してASYNCHRONOUSLYに
userQueue
タスクを実行できます。同期実行の場合、すべてのタスクが次々に順番に開始され、次のタスクが前のタスクの完了を明確に待っていることがわかります。さらに、最適化として、可能であれば、sync関数は現在のスレッドでクローズをトリガーでき、グローバルキューの優先順位は重要ではありません。それは私たちが見るものです。非同期実行の場合、タスクの完了とグローバルキューの優先順位を待たずにタスクが開始することがわかります。
sync
async
![](https://habrastorage.org/files/1be/bd5/993/1bebd59937b946f0994e23de7e096359.png)
sync
async
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
userQueue
での優先度の高いコードの実行
Playground
。その結果、タスク
userQueue
が頻繁に実行されることはありません。
3. 3番目の実験。プライベートシリアルキュー
グローバルキューに加え
Private
て、クラスイニシャライザーを使用してカスタムキューを作成できます
DispatchQueue
:カスタムキューを
![](https://habrastorage.org/files/415/932/073/4159320735f842c2bab1a2d3dd346772.png)
作成するときに指定する必要が
label
あるのは
Apple
、逆
DNS
表記(
“com.bestkora.mySerial”
)として指定することをお勧めする一意のラベルです。このキューはデバッガーでこの名前で表示されます。ただし、これはオプションであり、一意である限り任意の文字列を使用できます。キューの
label
初期化時以外に他の引数を指定しない場合
Private
、デフォルトで順次(
.serial
)キューが作成されます。キューを初期化するときに設定できる他の引数がありますが、それらについては少し後で説明します。
ユーザーどのように見て
Private
、一貫した場所
mySerialQueue
使用
sync
して
async
:メソッド
![](https://habrastorage.org/files/633/9f8/1cd/6339f81cd8f34041a240082f0545db47.png)
の場合は、同期
sync
の最適化機能としてあるため、私たちは型キュー実験3と同様の状況を見るには、重要ではありません
sync
、電流の流れの閉鎖をトリガすることができます。それは私たちが見るものです。メソッド
を使用し
async
、シーケンシャルキューが現在のキューに対して非同期に
mySerialQueue
タスクを実行できるように
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
するとどうなりますか?この場合、プログラムは停止せず、このタスクがキューで完了するまで待機しません
mySerialQueue
。管理はすぐにタスクを完了します
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
タスクと同時にそれらを実行します
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
4. QoS
レッツは、私たちの割り当て
Private
シリアル回線
serialPriorityQueue
、サービス品質QoSを.userInitiatedと等しく、非同期キューの最初の仕事に入れ
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
、その後、
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
この実験は、私たちの新しい場所があることを納得させるでしょう
serialPriorityQueue
、本当に一貫している、との使用にもかかわらず
async
方法、タスクは次々に実行されます到着順に:
![](https://habrastorage.org/files/0ab/fbc/7c0/0abfbc7c06ff46aca33529e91cdee03c.png)
したがって、マルチスレッドコードを実行するには、methodを使用するだけでは不十分
async
です。キューが異なるか、キュー自体が私は平行です(
.concurrent
)。並列(
.concurrent
)キューを使用した実験5では、プライベート並列(
.concurrent
)キューを使用した同様の実験が表示されます
workerQueue
、しかし、同じタスクをこのキューに入れると、まったく異なる状況になります。優先順位の異なる
順次
Private
キューを使用して
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
、最初にこのキューにタスクを非同期的に配置し、次にタスク
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
キュー
serialPriorityQueue1
c
qos .userInitiated
キュー
serialPriorityQueue2
c タスクの
qos .background
![](https://habrastorage.org/files/3a9/45e/00a/3a945e00a4d34d3d8d93d4dba5678ad9.png)
マルチスレッド実行があり、タスクはより
serialPriorityQueue1
高いサービス品質のキューで実行されることが多くなります
qos: .userIniatated
。たとえば、関数を使用して、サービスの品質を変更することにより、
任意のキューでのタスクの実行を
DispatchQueue
所定の時間遅延させることができます。
now() + 0.1
asyncAfter
qos
![](https://habrastorage.org/files/5d1/c46/136/5d1c4613699e4c3a92c7025ad92abce5.png)
5. 5番目の実験は、プライベート並列(同時)キューに関するものです。
Private
parallel(
.concurrent
)キューを初期化するには、Private queueを初期化するときに
attributes
等しい引数の値を示すだけで十分
.concurrent
です。この引数を指定しない場合、
Private
キューは順次(
.serial
)になります。引数
qos
も必須ではなく、問題なくスキップできます。同等のサービス品質を
並列キューに割り当て、タスクを最初にこのキューに非同期的に配置してから、新しい並列キューは実際に並列であり、その中のタスクは同時に実行されますが、4番目の実験(1つの順次回す
workerQueue
qos
.userInitiated
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
workerQueue
serialPriorityQueue
)、彼らは引数を
attributes
等しく設定します
.concurrent
:
![](https://habrastorage.org/files/a7c/8ef/137/a7c8ef1377424a3b8484f5f9cb62daf1.png)
絵は1つの連続したキューと比較して完全に異なります。その場合はすべてのタスクは、厳密にそれらが実行するために来た順序で行われているされている私たちの並列(マルチスレッド)キュー
workerQueue
複数のスレッドに「分割」、タスクが実際に並行して行うことができますいくつかのクエストを記号で
![](https://habrastorage.org/files/442/d34/28b/442d3428bf8744dfb4054f328f5c9d63.png)
、後にキューに入れられたとして、
workerQueue
並列スレッドでは高速です。
レッツ・使用並列
Private
異なる優先順位を持つキュー:
すべての
workerQueue1
C
qos .userInitiated
すべての
workerQueue2
C
qos .background
![](https://habrastorage.org/files/ae9/4b8/e9f/ae94b8e9f74847efb5b85c4291013e2a.png)
異なる配列を有すると同じパターン
Private
2回目の実験でバーストします。
workerQueue1
優先度の高いqueueでタスクが実行される頻度が高いことがわかります。
引数を使用して遅延実行でキューを作成
attributes
し、メソッドを使用して適切なタイミングでそのタスクの実行をアクティブ化できます
activate()
。
![](https://habrastorage.org/files/603/0b6/7be/6030b67be5ac4c53a3a1160f02c9b4c7.png)
6. 6番目の実験では、DispatchWorkItemオブジェクトを使用します。
Dispatch
キュー内のさまざまなタスクの実行を管理するための追加機能が必要な
DispatchWorkItem
場合は、サービス品質を設定できるタスクを作成できます
qos
。
![](https://habrastorage.org/files/faa/3f3/be0/faa3f3be05b34eaa9757c7499b708d2f.png)
これにより、パフォーマンスに影響します。
[.enforceQoS]
準備中にフラグを設定することにより
DispatchWorkItem
、そのタスクの
highPriorityItem
他のタスクよりもタスクの優先度が高くなります同じキュー:
![](https://habrastorage.org/files/b3e/019/897/b3e0198976ff4937a39e423f2545574c.png)
これにより、
Dispatch Queue
特定のサービス品質に対して特定のタスクの優先度を強制的に上げることができるため、
qos
「優先度の逆転」の現象に対処できます。 2つのタスク
highPriorityItem
が最新のタスクから開始されるという事実にもかかわらず、フラグ
[.enforceQoS]
と優先順位を高くすることにより、最初から実行されていることがわかります。
.userInteractive
。さらに、タスク
highPriorityItem
は異なるキューで複数回実行できます。
我々はフラグを削除した場合
[.enforceQoS]
:
![](https://habrastorage.org/files/c2f/83f/136/c2f83f1360724da2b015cb7532bfd7e8.png)
ジョブは
highPriorityItem
、サービスの品質になります
qos
彼らが実行するオンに設定されている。
![](https://habrastorage.org/files/1f3/d71/861/1f3d718618e04d438a14aaa209e7b465.png)
しかし、まだ彼らは非常に各キューの始まりに陥ります。これらすべての実験のコードは、GithubのfirstPlayground.playgroundにあります。
クラスで
DispatchWorkItem
プロパティがある
isCancelled
とメソッドの数を:
![](https://habrastorage.org/files/93f/5b4/a92/93f5b4a921ea45fb98d25370c5841a23.png)
メソッドの存在にもかかわらず
cancel()
のため
DispatchWorkItem
GCD
、まだ、すでに特定のライン上で開始された回路を、削除しません。現在できることは
DispatchWorkItem
、メソッドを使用して「リモート」としてマークすること
cancel()
です。メソッド呼び出しの場合
cancel()
前に発生した
DispatchWorkItem
方法によってキューに入れられます
async
、それは
DispatchWorkItem
実行されません。特定のキューで開始されたクロージャーを削除する方法がわからないという事実
Operation
だけでなく、メカニズムを使用する必要がある場合がある理由の1つです。クラスとそのメソッド、およびクラスインスタンスメソッドを使用できます。
GCD
GCD
DispatchWorkItem
notify (queue:, execute:)
DispatchQueue
async(execute workItem: DispatchWorkItem)
ポストの最初に提示された問題を解決するために-ネットワークからイメージをダウンロードします:特定のアドレスで「ネットワーク」からデータを取得することで構成されるクラスの
![](https://habrastorage.org/files/6de/817/317/6de8173174d74a5b997d631e6af28e49.png)
インスタンスの形式で同期タスクを形成します。関数を使用して、サービス品質で並列グローバルキューで非同期ジョブを実行する
workItem
DispatchWorlItem
data
imageURL
workItem
queue
qos: .utility
queue.async(execute: workItem)
機能を使用する
workItem.notify(queue: DispatchQueue.main) { if let imageData = data { eiffelImage.image = UIImage(data: imageData)} }
データアップロードの終了に関する通知を待っています
data
。これが起こったら、私たちは、画像要素を更新
UI
eiffelImage
:
![](https://habrastorage.org/files/a0d/c93/2d7/a0dc932d7c854e16a2c0cc62b7d93417.png)
コードが上に配置されているのGithub上LoadImage.playground。
パターン1.ネットワークから画像をダウンロードするためのコードオプション
2つの同期タスク
があります。ネットワークからデータを受信する
let data = try? Data(contentsOf: imageURL)
data
ユーザーインターフェイスデータに基づく更新(
UI
)
eiffelImage.image = UIImage(data: data)
これは
GCD
、コンポーネント
UIKit
がメインスレッドから排他的に機能するため、バックグラウンドスレッドで作業を行い、結果を表示するためにメインスレッドに返す必要がある場合に、マルチスレッドメカニズムを使用して実行される典型的なパターンです。
これは、従来の方法で行うことができます。
![](https://habrastorage.org/files/fe2/485/e6b/fe2485e6b0a445b6b798414564999dd6.png)
既製の非同期APIを使用するか、
URLSession
:
![](https://habrastorage.org/files/a86/b5d/d93/a86b5dd93d19429aa658ccc5a884f36a.png)
またはusingを使用し
DispatchWorlItem
ます。
![](https://habrastorage.org/files/541/3c2/28a/5413c228ae3a46f7a26d7bde29233ca9.png)
最後に、同期タスクを常に非同期「シェル」に「ラップ」して実行できます。
![](https://habrastorage.org/files/3e9/e50/ef0/3e9e50ef07434833b9c6f0ab5d48216f.png)
このパターンのコードはLoadImage.playgroundにありますgithubで。
パターン2。機能は、GCDを使用してテーブルビューおよびコレクションビューのネットワークから画像をダウンロードします。
一つだけで構成されて非常に簡単なアプリケーションの例を考えてみましょう
Image Table View Controller
細胞のみがインターネットからダウンロードされたイメージとブートプロセスを示す活動の指標が含まれているテーブル、:
![](https://habrastorage.org/files/2db/570/d4f/2db570d4f4f14f29977724fa12bf2f4e.png)
ここのクラスである
ImageTableViewController
スクリーンのサービスフラグメント
Image Table View Controller
:
![](https://habrastorage.org/files/c11/188/96e/c1118896ee43493eacd783e83e67b83d.png)
およびクラス
ImageTableViewCell
ロードされているテーブルセルについては、 image:
![](https://habrastorage.org/files/a21/015/ab4/a21015ab487f4bb8931db44a1a07eeed.png)
イメージは通常の古典的な方法でロードされます。クラスのモデル
ImageTableViewController
は8の配列です
URLs
:
- エッフェル塔
- ヴェネツィア
- スコットランドの城
- Cassiniサテライト-他のネットワークよりもはるかに長いネットワークからの読み込み
- エッフェル塔
- ヴェネツィア
- スコットランドの城
- 北極圏
アプリケーションを起動し、8つの画像すべてを表示するのに十分な速さでスクロールを開始すると、画面を離れるまでCassini Satelliteが起動しないことがわかります。明らかに、他のすべてのものよりもロードにかなり時間がかかります。
![](https://habrastorage.org/files/e31/90f/380/e3190f38002d4450b3fe892d3a083e0d.png)
しかし、最後までスクロールして最後のセルで「北極」を見た後、非常に短い時間の後、突然カッシーニ衛星に置き換えられることがわかりました。
![](https://habrastorage.org/files/4a5/067/031/4a50670314b94b65980d9a41ef15b151.png)
これは、このような単純なアプリケーションの誤った機能です。問題は何ですか?実際には、テーブルのセルはメソッドのおかげで再利用可能
dequeueReusableCell
です。セル(新規または再利用)が画面にヒットするたびに、ネットワークから非同期に画像がダウンロードされ(この時点で「ホイール」が回転します)、ダウンロードが完了して画像が受信されるとすぐに、
UI
このセルが更新されます。しかし、画像がロードされるのを待たずに、テーブルをスクロールし続けて、セル(「Cassini」)がそれ自体を更新せずに画面を離れます
UI
。ただし、新しい画像が下に表示され、画面を出た同じセルが再利用されますが、別の画像(「北極」)にはすぐにロードおよび更新されます
UI
。この時点で、このセルで以前に起動されたCassiniのダウンロードが返され、画面が更新されますが、これは間違っているためです。彼らはさまざまな時に戻ってきます:
![](https://habrastorage.org/files/154/a74/14a/154a7414aec94714b8c0a7e3e5d17b5c.png)
どうすれば状況を修正できますか?メカニズムのフレームワーク内で、
GCD
画面を離れたセルの画像の読み込みをキャンセルすることはできませんが
imageData
、ネットワークから来たときに
URL
、このデータの読み込みの原因
url
となったものを確認し、ユーザーが現時点でこのセルに入れたいものと比較できます
imageURL
:
![](https://habrastorage.org/files/c53/603/880/c536038803064446abf239b6d34ad445.png)
これですべてが正常に機能します。したがって、マルチスレッドプログラミングには非標準の想像力が必要です。実際、マルチスレッドプログラミングのいくつかのことは、コードが記述された順序とは異なる順序で行われます。付録GCDTableViewControllerは上にあるのGithub。
パターン3. DispatchGroupsの使用
非同期に実行する必要があるタスクがいくつかあり、それらが完了するのを待つ場合、
DispatchGroup
作成が非常に簡単なグループが適用されます。
let imageGroup = DispatchGroup()
我々は、「ネットワークから」4つの異なる画像をダウンロードする必要があるとしましょう:
![](https://habrastorage.org/files/0bf/bc9/693/0bfbc9693dd84808b7ffa384cae062e7.png)
メソッドは、
queue.async(group: imageGroup)
グループに任意の割り当てを追加することができます(同期)任意の行で実行されている
queue
:
![](https://habrastorage.org/files/d0f/a06/f93/d0fa06f933f947d79efbb1fc014d7042.png)
私たちは、グループを作成
imageGroup
し、法によって、このグループに配置された
async (group: imageGroup)
グローバルなパラレルターンへの非同期ロードの画像のための2つの割り当て
DispatchQueue.global()
そして
DispatchQueue.global(qos:.userInitiated)
、サービス品質でグローバル並列キューにイメージを非同期的にロードする2つのタスク
.userInitiated
。異なるキューで実行されているタスクを同じグループに追加できることが重要です。グループ内のすべてのタスクが完了すると、関数
notify
が呼び出されます-これはグループ全体の一種のコールバックブロックであり、すべての画像を同時に画面に配置します。
![](https://habrastorage.org/files/3d3/15b/0df/3d315b0df6eb4cbeb5b15edf19ad4343.png)
グループにはスレッドセーフな内部カウンターが含まれており、メソッドを使用してタスクがグループに追加されると自動的に増加します
async (group: imageGroup)
。タスクが実行されると、カウンターが1つ減り、すべての長期操作が完了した後にコールバックブロックが呼び出されることが保証されます。同期操作のグループの形成に関する実験は、GithubのPlayground GroupSyncTasks.playgroundで提示されます。
グループに同期操作だけでなく非同期操作もある場合は、スレッドセーフカウンターを手動で制御できます。
enter()
メソッドはカウンターを増やし、メソッドは
leave()
減らします。GithubのPlayground GroupAsyncTasks.playgroundを使用して、グループ内の非同期操作の配置を学習します。非同期タスクをグループに配置し、画面の上部に表示します。
![](https://habrastorage.org/files/8dc/606/902/8dc606902dd449c0985938b06eaf2322.png)
比較のために、画面の下部に、通常の方法で取得した同じ画像をグループにまとめずに次々に配置します。あなたはすぐに先頭に同じ画像の外観の違いを感じるだろうし、下部にある:初めにメソッド呼び出しが、次々と、画面下部の画像、および画面のすべて一度画像トップがあるでしょう
asyncGroup ()
し、
asyncUsual ()
逆の順序で:
![](https://habrastorage.org/files/e1e/bc2/058/e1ebc205827843aa9e40c0fd504c5ae7.png)
打込みを混合操作のグループ:同期および非同期:
![](https://habrastorage.org/files/2f8/380/97b/2f838097b5ae4de0af9e0e9e1bd84111.png)
結果は同じになります。
パターン4.スレッドセーフ変数。分離キュー
のは、バーストで私たちの最初の実験に戻って行こう
GCD
で
Swift 3
:と行のタスクの時系列(垂直方向)のシーケンスを維持し、それによって、水平な方法で異なるライン上のタスクの結果をユーザに提示しようと
![](https://habrastorage.org/files/b71/39e/908/b7139e908bf94316a945bac9307eca06.png)
、私は通常どおりの結果格納するために使用されることを言っているNEpotochno-をセキュアで
Swift 3
、文字列
usualString: String
、およびスレッドセーフ(スレッドセーフ)文字列
safeString: ThreadSafeString
:
var safeString = ThreadSafeString("") var usualString = ""
このセクションの目的は、スレッドセーフな文字列をでどのように配置するかを示す
Swift 3
ことです。これについては後で詳しく説明します。
スレッドセーフ文字列を使用したすべての実験は、GithubのPlayground GCDPlayground.playgroundで行われます。
Iが蓄積両方の行の情報を順にビット割り当てを変更します
usualString
と
safeString
:
![](https://habrastorage.org/files/ac0/abd/a30/ac0abda30db941f998a8196fa161b04e.png)
で
Swift
キーワードで宣言された任意の変数が
let
一定であるため、スレッドセーフ(
thread-safe
)。キーワード
var
を使用して変数を宣言すると、変数は可変(
mutable
)および非スレッドセーフ(
thread-safe
)特別に設計されるまで。2つのスレッドが同じメモリブロックを同時に変更し始めると、このメモリブロックが破損する可能性があります。さらに、あるスレッドで変数の値が別のスレッドで更新されているときに変数を読み取ると、「古い値」、つまり競合状態(
race condition
)が読み取られる危険があります。
スレッドセーフの理想的なオプションは、次の場合です。
- 読み取りは同期的に発生し、マルチスレッド化されます
- レコードは非同期でなければならず、現在この変数で動作する唯一のタスクでなければなりません
幸いなことに、
GCD
バリア(
barrier
)と分離キューを使用して解決するエレガントな方法を提供します。
![](https://habrastorage.org/files/cb5/76f/612/cb576f61217b4578a9d80ba2870e4a6a.png)
バリア
GCD
は1つの興味深いことを行います。キューが完全に空になる瞬間を待ってから、クローズを実行します。バリアーがクロージャーの完了を開始するとすぐに、キューがこの時間中に他のクロージャーを実行しないようにし、基本的に同期関数として機能します。バリアによるクロージャが終了するとすぐに、キューは通常の動作に戻り、読み取りまたは別の書き込みと同時に記録が実行されないことが保証されます。
のは、それがスレッドセーフなクラスをどのように見えるかを見てみましょう
ThreadSafeString
:
![](https://habrastorage.org/files/cce/777/b6d/cce777b6db6a4af191494b44ed5ee959.png)
関数
isolationQueue.sync
「読み」回路を送信するために
{result = self.internalString}
、分離の私たちの場所を
isolationQueue
して結果を返す前に、終了を待っています
result
。その後、読み取り結果が得られます。同期呼び出しを行わない場合は、コールバックブロックを導入する必要があります。キューは
isolationQueue
並列(
.concurrent
)であるため、このような同期読み取りは一度に数回実行できます。
この関数
isolationQueue.async (flags: .barrier)
は、「record」、「add」、または「initialize」のクローズを分離キューに送信します
isolationQueue
。この関数
async
は、「レコード」、「追加」、または「初期化」のクローズが実際に行われる前に制御が返されることを意味します。バリア部分
(flags: .barrier)
は、キュー内の各クロージャーの実行が完了するまでクロージャーが実行されないことを意味します。その他の障害は、バリアの後に配置され、バリアが完了した後に実行されます。
実験の結果
DispatchQueues
、スレッドセーフな(提示
thread-safe
)ライン
safeString: ThreadSafeString
と法線は
usualString: String
、上にあるのGithub上の遊び場GCDPlayground.playground。
これらの結果を見てみましょう。
1.関数
sync
グローバル平行線で
DispatchQueue.global(qos: .userInitiated)
に関して
Playground
:
![](https://habrastorage.org/files/5b6/71a/9a6/5b671a9a6f0945188497cb7bab6584f9.png)
従来の非ストリームセーフラインの結果
usualString
、MATCHのスレッドセーフな文字列に結果
safeString
。
2.関数
async
グローバル平行線で
DispatchQueue.global(qos: .userInitiated)
に関して
Playground
:
![](https://habrastorage.org/files/7a5/8f4/edb/7a58f4edbdc8404998d4550a546f5fe0.png)
従来の非ストリームラインセーフの結果
usualString
、DO NOT MATCHスレッドセーフな文字列で結果を取得します
safeString
。
3.機能
sync
の
Private
シリアルライン
DispatchQueue (label: "com.bestkora.mySerial")
遊び場に関連して:
![](https://habrastorage.org/files/d9d/8e9/d59/d9d8e9d597e640a5a29b4f09b6c2d18f.png)
平野上の結果は、スレッドセーフではありませんライン
usualString
、MATCHの結果をスレッドセーフな文字列に
safeString
。
4.機能
async
上の
Private
シリアルライン
DispatchQueue (label: "com.bestkora.mySerial")
に関して
Playground
:
![](https://habrastorage.org/files/009/08d/c07/00908dc0791a4192bd2517af0123bb52.png)
従来の非ストリームラインセーフの結果
usualString
、DO NOT MATCHスレッドセーフな文字列に結果を
safeString
。
5.
async
タスクの機能
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
そして
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
上の
Private
シリアル回線
DispatchQueue (label: "com.bestkora.mySerial", qos : .userInitiated)
:
![](https://habrastorage.org/files/f15/016/125/f150161255004ed8bf0b28a906a7a226.png)
結果は、スレッドセーフなライン上に、通常ではありません
usualString
、MATCHのスレッドセーフな文字列の結果が
safeString
。
6.
async
タスクの機能
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
そして
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
異なるの
Private
連続したライン
DispatchQueue (label: "com.bestkora.mySerial", qos : .userInitiated)
、および
DispatchQueue (label: "com.bestkora.mySerial", qos : .background)
:
![](https://habrastorage.org/files/862/f80/3d3/862f803d306441809b432fb23072c4b7.png)
従来の非ストリームライン金庫の結果
usualString
、DO NOT MATCHスレッドセーフな文字列の結果と
safeString
。
7.
async
タスクの機能
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
そして
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
上の
Private
平行線
DispatchQueue (label: "com.bestkora.mySerial", qos : .userInitiated, attributes: .concurrent)
:
![](https://habrastorage.org/files/0f8/fb3/d84/0f8fb3d8462641abb5dac4c97ba1ab34.png)
従来の非ストリームライン金庫の結果
usualString
、DO NOT MATCHスレッドセーフな文字列の結果と
safeString
。
8.
async
タスクの機能
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
そして
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
andを使用した異なる
Private
並列キューで:通常のスレッドセーフラインでの結果、スレッドセーフラインでの結果と一致しない。9.機能の優先順位でCの変更:従来の非ストリームライン金庫の結果、DO NOT MATCHスレッドセーフな文字列の結果と。10.機能の優先順位のC変更:普通スレッドセーフではない線上結果、MATCHのスレッドセーフライン上の結果ジョブとして
qos : .userInitiated
qos : .background
![](https://habrastorage.org/files/f65/fbe/1f2/f65fbe1f2ae244499f4e8988533e71b0.png)
usualString
safeString
asyncAfter (deadline: .now() + 0.0, qos: .userInteractive)
![](https://habrastorage.org/files/d81/24c/668/d8124c6682e84ef99ab04c3e64779e65.png)
usualString
safeString
asyncAfter (deadline: .now() + 0.1, qos: .userInteractive)
![](https://habrastorage.org/files/ed2/11c/24b/ed211c24b2144dba8576feac5b45ed13.png)
usualString
safeString
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
そして
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
時間間隔で。
マルチスレッドの割り当てがある場所
![](https://habrastorage.org/files/01a/499/a93/01a499a9300e49d6ada90f665225560d.png)
そして
![](https://habrastorage.org/files/1cc/369/457/1cc369457f1c4804b1c13f653a404e75.png)
異なるキューで発生するか、1つの並列(
.concurrent
)キューで発生する
usualString
と、通常の行とスレッドセーフな行の間で不一致が発生します
safeString
。
スレッドセーフな文字列を使用して
safeString
、我々はいわば、キューのプロパティとの同期と非同期の機能を見ることができ、右側の「鳥瞰」は、対応するタスクの実行は、次のとおりです。
![](https://habrastorage.org/files/0ef/0ea/0d9/0ef0ea0d969a4773944a1aa349db246e.png)
あなたが使用していない場合は
Playground
、アプリケーションを、そして
Xcode 8
それを使用することが可能である
Thread Sanitizer
決意のために
race condition
。
Thread Sanitizer
アプリケーション実行の段階で動作します。スキーム(
Scheme
)を編集
![](https://habrastorage.org/files/dac/d23/58e/dacd2358ebd943b98c43ba37ff659e91.png)
することで開始できます。この例では、競合状態の検出が表示されます。Tsanアプリケーションコードはオンですgithubの。
結論マルチスレッドプログラミングの問題を解決
する
GCD
ためのいくつかのユースケースを検討しました
Swift 3
。次の記事では
Operations
、マルチスレッドプログラミングの実際の使用に関する質問について説明します
Swift 3
。
PS現在
GCD API
すべてのプラットフォームで利用可能で、マルチスレッドアプリケーションを作成する優れた方法を提供します。しかし、現在のバージョンに
Swift 3
は、マルチスレッドを記述するための構文構造がありません。開発チーム
Swift
は、マルチスレッドをより集中的に取り上げ、バージョン
Swift 5
(2018)のマルチスレッド構文の実際の変更を準備し、2017年春/夏に議論を開始し、マニフェスト「
2017年の秋まで。IBM のプログラミング言語の日でクリス・ルトナーは、既存のマルチスレッドプログラミング
GCD API
と
async
関数を使用すると、「運命のピラミッド」につながり、コメントなしでどのデータ/状態を認識するのが非常に難しいと述べました」独自の「何
Dispatch Queue
や、関連するタスクは、これらの行によって行わ:
![](https://habrastorage.org/files/dae/7f5/438/dae7f54383334d2a85182a41c0cc4c76.png)
改善マルチスレッドのための可能な分野の一つ-の使用である俳優のモデル(俳優、モデルである。)各
actor
-で、実際には、
DispatchQueue
+ ステータスこの場所が動作し、+ オペレーションこの行で実行:
![](https://habrastorage.org/files/16e/979/4d8/16e9794d819342358fff64df5586ee4c.png)
しかし、これは多くのオファーの1つにすぎません。いま、検討する
actors
、
async/await
、不可分性を、メモリモデルその他の関連トピック。マルチスレッドは、クライアントとサーバーの両方で新しいアプローチへの「扉を開く」ため、非常に重要です。ここで
進化
Swift
を見ることができます。
この記事は、マルチスレッドプログラミングが大量にあるため、iTunesでホストされているStanford CS193p Winter 17コース(iOS 10およびSwift 3に基づく)を使用して、SwiftでiOSプログラミングを学習する人に役立つ場合があります。
参照資料
WWDC 2016. Concurrent Programming With GCD in Swift 3 (session 720)
WWDC 2016. Improving Existing Apps with Modern Best Practices (session 213)
WWDC 2015. Building Responsive and Efficient Apps with GCD.
Grand Central Dispatch (GCD) and Dispatch Queues in Swift 3
iOS Concurrency with GCD and Operations
The GCD Handbook
GCD
Modernize libdispatch for Swift 3 naming conventions
GCD
GCD – Beta
CONCURRENCY IN IOS
www.uraimo.com/2017/05/07/all-about-concurrency-in-swift-1-the-present
All about concurrency in Swift — Part 1: The Present