Swift 3の同時実行性。GCDおよびディスパッチキュヌ

iOSのマルチスレッド 同時実行は、iOSアプリケヌション開発者ぞのむンタビュヌ䞭に尋ねられる質問、およびiOSアプリケヌションを開発する際にプログラマヌが犯す最倧の間違いに垞に含たれおいるず蚀わなければなりたせん。 したがっお、このツヌルを完党にマスタヌするこずが非垞に重芁です。
したがっお、アプリケヌションがあり、 main threadで実行されたす。 main threadは、ナヌザヌむンタヌフェむス UI を衚瀺するコヌドを実行したす。 ネットワヌクからデヌタをダりンロヌドしたり、 main thread メむンスレッドで画像を凊理するなど、アプリケヌションに「時間のかかる」コヌドを远加し始めるずすぐに、 UIの動䜜が倧幅に遅くなり、完党な「フリヌズ」に至るこずさえありたす。



このような問題が発生しないように、アプリケヌションのアヌキテクチャを倉曎するにはどうすればよいですか この堎合、マルチスレッド 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" 。この䞊列凊理の技術的な実装に入らずに、盞互に関連するタスクの順序を参照したす。



serial シヌケンシャルキュヌでは、クロヌゞャヌは実行された順序で厳密に完了したすが、 concurrent パラレルキュヌでは、ゞョブは予枬䞍胜な方法で終了したす。 さらに、 serialキュヌ䞊の特定のタスクグルヌプの合蚈実行時間が、 concurrentキュヌ䞊の同じタスクグルヌプの実行時間を倧幅に超えおいるこずがわかりたす。 珟圚のserial シヌケンシャルキュヌでは、1぀のタスクのみが実行され、 concurrent パラレルキュヌでは、珟圚の時間のタスク数が倉曎できたす。

同期および非同期のタスク実行


queue䜜成されるず、 async珟圚のキュヌに関連する同期実行ずasync珟圚のキュヌに関連する非同期実行の2぀の関数を䜿甚しおタスクをqueueに配眮できたす。

同期sync関数は、タスクが完了した埌にのみ制埡を珟圚のキュヌに返し、珟圚のキュヌをブロックしたす。



async機胜は、 sync機胜ずは察照的に、別のキュヌでゞョブを開始した盎埌に、完了するのを埅たずに、珟圚のキュヌに制埡を戻したす。 したがっお、 async機胜は、珟圚のキュヌでのタスクの実行をブロックしたせん。



非同期実行の堎合、シヌケンシャル serial キュヌずしおの「別のキュヌ」になるこずがありたす。



および䞊列 concurrent キュヌ



開発者のタスクは、 sync機胜を䜿甚しおsync的にたたは非同期機胜を䜿甚しおasync的にキュヌを遞択し、タスク通垞はクロヌゞャヌをこのキュヌに远加するこずのみです。その埌、 iOSのみが機胜したす。

この投皿の最初に瀺したタスクに戻り、「ネットワヌクからのデヌタ」ネットワヌクから別のキュヌにデヌタを受信するタスクの実行を切り替えたす。



別のDispatch QueueネットワヌクからDataを受信した埌、 Main thread送り返しMain thread 。



別のDispatchQueueキュヌでネットワヌクからDataを受信するず、 Main threadは解攟され、 UIで発生するすべおのむベントを凊理したす。 この堎合の実際のコヌドがどのように芋えるか芋おみたしょう



かなりの時間がかかり、 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 。


たた、デフォルトの.default concurrencyキュヌ.defaultがありたす。これは、 qos 「サヌビス品質」 qos䞍足を報告しqos 。 挔算子を䜿甚しお䜜成されたす
 DispatchQueue.global() 

他の゜ヌスからqos情報を刀別できる堎合はそれが䜿甚され、そうでない堎合は.userInitiatedず.utility間でqos䜿甚されたす。

これらのグロヌバルキュヌはすべおSYSTEMグロヌバルキュヌであり、このキュヌ内のタスクは私たちのタスクだけではないこずを理解するこずが重芁です。 たた、1぀を陀くすべおのグロヌバルキュヌがconcurrent 䞊列キュヌであるこずを知るこずも重芁です。

ナヌザヌむンタヌフェむス甚の特別なグロヌバルシリアルキュヌ-メむンキュヌ


Appleは、唯䞀のグロヌバルserial シヌケンシャルキュヌを提䟛しおいたす-これは、䞊蚘のMain queueです。 このキュヌでは、 UI倉曎に関連しないリ゜ヌス集玄的な操䜜たずえば、ネットワヌクからのデヌタのダりンロヌドを実行しお、この操䜜の間UIを「フリヌズ」せず、ナヌザヌむンタヌフェむスをい぀でもナヌザヌの操䜜に応答させないようにするこずは望たしくありたせん。ゞェスチャヌで。



このようなリ゜ヌス集䞭型の操䜜を他のスレッドたたはキュヌに「流甚」するこずを匷くお勧めしたす。



もう1぀厳しい芁件がありたす。 Main queueのみ、 UI芁玠を倉曎できたす。

これは、 Main queue UIアクションに「応答する」だけでなくはい、これが䞻な理由、ナヌザヌむンタヌフェヌスをマルチスレッド環境での「混乱」から保護する、぀たり、ナヌザヌアクションは、厳密に順番に実行されたす。 UI芁玠が異なるキュヌでアクションを実行できるようにするず、描画が異なる速床で発生し、アクションが亀差し、画面䞊で完党に予枬䞍胜になる可胜性がありたす。 Main queueを䞀皮の「同期ポむント」ずしお䜿甚し、画面に「描画」したいすべおのナヌザヌが戻りたす。

マルチスレッドの問題


タスクの䞊列動䜜を蚱可するずすぐに、異なるタスクが同じリ゜ヌスにアクセスしたいずいう事実に関連する問題がありたす。
䞻に3぀の問題がありたす。



競合状態


privateキュヌでvalue倉数を非同期に倉曎し、珟圚のスレッドでvalueを衚瀺するず、 race condition最も単玔なケヌスを再珟できたす。



通垞の倉数valueずそれを倉曎する通垞の関数changeValueがあり、意図的にsleep(1)挔算子を䜿甚しお、倉数の倉曎にかなりの時間がかかるようにしたした。 asyncを䜿甚しおchangeValue関数をchangeValue実行するず、倉曎されたvalue倉数に配眮する前に、珟圚のスレッドでvalue倉数を別の倀にリセットできたす。これがrace conditionです。 このコヌドは、次の圢匏での印刷に察応しおいたす。



「 race condition 」ず呌ばれる珟象を明確に瀺す図



asyncメ゜ッドをsync眮き換えたしょう



印刷ず結果の䞡方が倉曎されたした

<img

そしお、「 race condition 」ず呌ばれる珟象がない図



キュヌのsyncメ゜ッドには非垞に泚意する必芁がありたすが、「珟圚のスレッド」はゞョブが他のキュヌで終了するたで埅機するため、 syncメ゜ッドは競合状態を避けるために非垞に䟿利です。 race condition珟象をシミュレヌトするコヌドは、GithubのfirstPlayground.playgroundで衚瀺できたす。 埌で、異なるストリヌムで受信した文字列を圢成するずきの実際の「 race condition 」を瀺したす。 「バリア」を䜿甚しおストリングを圢成する゚レガントな方法も提案されたす。これにより、「 race conditions 」を回避し、生成されたストリングをスレッドセヌフにするこずができたす。

優先順䜍の逆転


の抂念は、リ゜ヌスのロックに密接に関連しおいたす。



優先床が䜎Aず高Bの2぀のタスクがシステムにあるずしたす。時間T1で、タスクAはリ゜ヌスをブロックし、リ゜ヌスの提䟛を開始したす。時間T2で、タスクBは優先床の䜎いタスクAを抌し出し、時間T3でリ゜ヌスを匕き継ぎたす。ただし、リ゜ヌスがロックされおいるため、タスクBは保留され、タスクAは実行を継続したす。時間T4で、タスクAはリ゜ヌスのサヌビスを完了し、ロックを解陀したす。タスクBはリ゜ヌスを予期しおいるため、すぐに実行を開始したす。
タむムスパンT4-T3は、限定優先順䜍反転ず呌ばれたす。この期間には、蚈画ルヌルず論理的な矛盟がありたす。優先床の䜎いタスクが実行されおいる間、優先床の高いタスクが埅機しおいたす。

しかし、これは最悪ではありたせん。システムで3぀のタスクが機胜するずしたす䜎優先床A、䞭優先床B、高優先床C



リ゜ヌスがタスクAによっおブロックされ、タスクCによっお必芁ずされる堎合、同じ状況が芳察されたす-高優先床タスクがブロックされたすしかし、タスクBがリ゜ヌスを埅機するC埌Aに取っお代わったずしたす。タスクBは競合に぀いお䜕も知らないので、䞀定期間T5-T4にわたっお奜きなだけ実行できたす。さらに、Bに加えお、システムには他のタスクがあり、優先床はAよりも倧きくB䜎くなりたす。したがっお、期間T6-T3の期間は䞀般に無期限です。この状況は、無制限の優先順䜍反転ず呌ばれたす。

䞀般に、優先床の制限の反転は避けるこずはできたせんが、マルチスレッドアプリケヌションにずっお無制限のものほど危険ではありたせん。優先床の䜎いすべおの「干枉」タスクの優先床を匷制的に䞊げるこずで排陀されたす。

以䞋に、DispatchWorkItemオブゞェクトを䜿甚しお珟圚のキュヌ内の個々のタスクの優先床を䞊げる方法を瀺したす。

デッドロック


デッドロックは、リ゜ヌスロックがネストされおいるずきに発生する可胜性があるシステムの緊急状態です。システムに2぀のリ゜ヌスXおよびYを䜿甚する䜎Aおよび高B優先床の2぀の



タスクがあるずしたす。時間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。Playgroundcコマンドで䜕らかのむベントを埅぀のを止めるこずができPlaygroundPage.current.finishExecution()たす。
別のクヌルな機胜がありたすPlayground- UIチヌムの助けを借りお「生きおいる」ず察話する機胜
 PlaygroundPage.liveView = viewController 

およびアシスタント゚ディタヌAssistant Editor。たずえば、を䜜成する堎合、viewControllerを確認するには、コヌドを無制限に実行viewControllerするように構成Playgroundし、アシスタント゚ディタヌを有効にするだけですAssistant Editor。コマンドをコメント化しお、手動でPlaygroundPage.current.finishExecution()停止するPlayground必芁がありたす。



Playground実隓環境テンプレヌトのcコヌドは、EnvironmentPlayground.playgroundずいう名前で、Githubにありたす。

1.最初の実隓。グロヌバルキュヌずゞョブ


簡単な実隓から始めたしょう。䞀貫性のある1我々はたた、䞖界的なキュヌの数を定矩するmainQueueこずがある- main queue、そしお4぀の䞊列concurrentqueues- 、、userInteractiveQueue ず。デフォルトで蚭定できたす- ゞョブずしお、10個の同䞀の文字ず珟圚のキュヌの優先床を印刷するこずを遞択したす。1文字を印刷する別のタスク、高い優先床で実行したす。userQueueutilityQueuebackgroundQueueconcurrent queuedefautQueue



tasktaskHIGH



2.第二の実隓は、に適甚されたす同期および非同期的にグロヌバルキュヌに


たずえば、グロヌバルキュヌを受け取ったら、methodを䜿甚しおSYNCHRONOUSLYに、たたはmethodを䜿甚しおASYNCHRONOUSLYにuserQueueタスクを実行できたす。同期実行の堎合、すべおのタスクが次々に順番に開始され、次のタスクが前のタスクの完了を明確に埅っおいるこずがわかりたす。さらに、最適化ずしお、可胜であれば、sync関数は珟圚のスレッドでクロヌズをトリガヌでき、グロヌバルキュヌの優先順䜍は重芁ではありたせん。それは私たちが芋るものです。非同期実行の堎合、タスクの完了ずグロヌバルキュヌの優先順䜍を埅たずにタスクが開始するこずがわかりたす。syncasync



sync

async



userQueueでの優先床の高いコヌドの実行Playground。その結果、タスクuserQueueが頻繁に実行されるこずはありたせん。

3. 3番目の実隓。プラむベヌトシリアルキュヌ


グロヌバルキュヌに加えPrivateお、クラスむニシャラむザヌを䜿甚しおカスタムキュヌを䜜成できたすDispatchQueueカスタムキュヌを



䜜成するずきに指定する必芁がlabelあるのはApple、逆DNS衚蚘“com.bestkora.mySerial”ずしお指定するこずをお勧めする䞀意のラベルです。このキュヌはデバッガヌでこの名前で衚瀺されたす。ただし、これはオプションであり、䞀意である限り任意の文字列を䜿甚できたす。キュヌのlabel初期化時以倖に他の匕数を指定しない堎合Private、デフォルトで順次.serialキュヌが䜜成されたす。キュヌを初期化するずきに蚭定できる他の匕数がありたすが、それらに぀いおは少し埌で説明したす。
ナヌザヌどのように芋おPrivate、䞀貫した堎所mySerialQueue䜿甚syncしおasyncメ゜ッド



の堎合は、同期 syncの最適化機胜ずしおあるため、私たちは型キュヌ実隓3ず同様の状況を芋るには、重芁ではありたせんsync、電流の流れの閉鎖をトリガするこずができたす。それは私たちが芋るものです。メ゜ッド

を䜿甚しasync、シヌケンシャルキュヌが珟圚のキュヌに察しお非同期にmySerialQueueタスクを実行できるように

するずどうなりたすかこの堎合、プログラムは停止せず、このタスクがキュヌで完了するたで埅機したせんmySerialQueue。管理はすぐにタスクを完了したす

タスクず同時にそれらを実行したす


4. QoS


レッツは、私たちの割り圓おPrivateシリアル回線serialPriorityQueue、サヌビス品質QoSを.userInitiatedず等しく、非同期キュヌの最初の仕事に入れ

、その埌、

この実隓は、私たちの新しい堎所があるこずを玍埗させるでしょうserialPriorityQueue、本圓に䞀貫しおいる、ずの䜿甚にもかかわらずasync方法、タスクは次々に実行されたす到着順に



したがっお、マルチスレッドコヌドを実行するには、methodを䜿甚するだけでは䞍十分asyncです。キュヌが異なるか、キュヌ自䜓が私は平行です.concurrent。䞊列.concurrentキュヌを䜿甚した実隓5では、プラむベヌト䞊列.concurrentキュヌを䜿甚した同様の実隓が衚瀺されたすworkerQueue、しかし、同じタスクをこのキュヌに入れるず、たったく異なる状況になりたす。優先順䜍の異なる

順次Privateキュヌを䜿甚しお

、最初にこのキュヌにタスクを非同期的に配眮し、次にタスク


キュヌserialPriorityQueue1c qos .userInitiated
キュヌserialPriorityQueue2c タスクのqos .background



マルチスレッド実行があり、タスクはよりserialPriorityQueue1高いサヌビス品質のキュヌで実行されるこずが倚くなりたすqos: .userIniatated。たずえば、関数を䜿甚しお、サヌビスの品質を倉曎するこずにより、

任意のキュヌでのタスクの実行をDispatchQueue所定の時間遅延させるこずができたす。now() + 0.1asyncAfterqos



5. 5番目の実隓は、プラむベヌト䞊列同時キュヌに関するものです。


Privateparallel.concurrentキュヌを初期化するには、Private queueを初期化するずきにattributes等しい匕数の倀を瀺すだけで十分.concurrentです。この匕数を指定しない堎合、Privateキュヌは順次.serialになりたす。匕数qosも必須ではなく、問題なくスキップできたす。同等のサヌビス品質を

䞊列キュヌに割り圓お、タスクを最初にこのキュヌに非同期的に配眮しおから、新しい䞊列キュヌは実際に䞊列であり、その䞭のタスクは同時に実行されたすが、4番目の実隓1぀の順次回すworkerQueueqos.userInitiated



workerQueueserialPriorityQueue、圌らは匕数をattributes等しく蚭定したす.concurrent



絵は1぀の連続したキュヌず比范しお完党に異なりたす。その堎合はすべおのタスクは、厳密にそれらが実行するために来た順序で行われおいるされおいる私たちの䞊列マルチスレッドキュヌworkerQueue耇数のスレッドに「分割」、タスクが実際に䞊行しお行うこずができたすいく぀かのク゚ストを蚘号で

、埌にキュヌに入れられたずしお、workerQueue䞊列スレッドでは高速です。

レッツ・䜿甚䞊列Private異なる優先順䜍を持぀キュヌ

すべおのworkerQueue1C qos .userInitiated
すべおのworkerQueue2C qos .background



異なる配列を有するず同じパタヌンPrivate2回目の実隓でバヌストしたす。workerQueue1優先床の高いqueueでタスクが実行される頻床が高いこずがわかりたす。

匕数を䜿甚しお遅延実行でキュヌを䜜成attributesし、メ゜ッドを䜿甚しお適切なタむミングでそのタスクの実行をアクティブ化できたすactivate()。



6. 6番目の実隓では、DispatchWorkItemオブゞェクトを䜿甚したす。


Dispatchキュヌ内のさたざたなタスクの実行を管理するための远加機胜が必芁なDispatchWorkItem堎合は、サヌビス品質を蚭定できるタスクを䜜成できたすqos。



これにより、パフォヌマンスに圱響したす。[.enforceQoS]準備䞭にフラグを蚭定するこずによりDispatchWorkItem、そのタスクのhighPriorityItem他のタスクよりもタスクの優先床が高くなりたす同じキュヌ



これにより、Dispatch Queue特定のサヌビス品質に察しお特定のタスクの優先床を匷制的に䞊げるこずができるため、qos「優先床の逆転」の珟象に察凊できたす。 2぀のタスクhighPriorityItemが最新のタスクから開始されるずいう事実にもかかわらず、フラグ[.enforceQoS]ず優先順䜍を高くするこずにより、最初から実行されおいるこずがわかりたす。.userInteractive。さらに、タスクhighPriorityItemは異なるキュヌで耇数回実行できたす。

我々はフラグを削陀した堎合[.enforceQoS]



ゞョブはhighPriorityItem、サヌビスの品質になりたすqos圌らが実行するオンに蚭定されおいる。



しかし、ただ圌らは非垞に各キュヌの始たりに陥りたす。これらすべおの実隓のコヌドは、GithubのfirstPlayground.playgroundにありたす。

クラスでDispatchWorkItemプロパティがあるisCancelledずメ゜ッドの数を



メ゜ッドの存圚にもかかわらずcancel()のためDispatchWorkItem GCD、ただ、すでに特定のラむン䞊で開始された回路を、削陀したせん。珟圚できるこずはDispatchWorkItem、メ゜ッドを䜿甚しお「リモヌト」ずしおマヌクするこずcancel()です。メ゜ッド呌び出しの堎合cancel()前に発生したDispatchWorkItem方法によっおキュヌに入れられたすasync、それはDispatchWorkItem実行されたせん。特定のキュヌで開始されたクロヌゞャヌを削陀する方法がわからないずいう事実Operationだけでなく、メカニズムを䜿甚する必芁がある堎合がある理由の1぀です。クラスずそのメ゜ッド、およびクラスむンスタンスメ゜ッドを䜿甚できたす。GCDGCD

DispatchWorkItemnotify (queue:, execute:)DispatchQueue

 async(execute workItem: DispatchWorkItem) 

ポストの最初に提瀺された問題を解決するために-ネットワヌクからむメヌゞをダりンロヌドしたす特定のアドレスで「ネットワヌク」からデヌタを取埗するこずで構成されるクラスの



むンスタンスの圢匏で同期タスクを圢成したす。関数を䜿甚しお、サヌビス品質で䞊列グロヌバルキュヌで非同期ゞョブを実行するworkItemDispatchWorlItemdataimageURLworkItemqueueqos: .utility
 queue.async(execute: workItem) 


機胜を䜿甚する
 workItem.notify(queue: DispatchQueue.main) { if let imageData = data { eiffelImage.image = UIImage(data: imageData)} } 

デヌタアップロヌドの終了に関する通知を埅っおいたすdata。これが起こったら、私たちは、画像芁玠を曎新UI eiffelImage



コヌドが䞊に配眮されおいるのGithub侊LoadImage.playground。

パタヌン1.ネットワヌクから画像をダりンロヌドするためのコヌドオプション


2぀の同期タスク
がありたす。ネットワヌクからデヌタを受信する
 let data = try? Data(contentsOf: imageURL) 

dataナヌザヌむンタヌフェむスデヌタに基づく曎新UI
 eiffelImage.image = UIImage(data: data) 

これはGCD、コンポヌネントUIKitがメむンスレッドから排他的に機胜するため、バックグラりンドスレッドで䜜業を行い、結果を衚瀺するためにメむンスレッドに返す必芁がある堎合に、マルチスレッドメカニズムを䜿甚しお実行される兞型的なパタヌンです。

これは、埓来の方法で行うこずができたす。



既補の非同期APIを䜿甚するか、URLSession



たたはusingを䜿甚しDispatchWorlItemたす。



最埌に、同期タスクを垞に非同期「シェル」に「ラップ」しお実行できたす。



このパタヌンのコヌドはLoadImage.playgroundにありたすgithubで。

パタヌン2。機胜は、GCDを䜿甚しおテヌブルビュヌおよびコレクションビュヌのネットワヌクから画像をダりンロヌドしたす。


䞀぀だけで構成されお非垞に簡単なアプリケヌションの䟋を考えおみたしょうImage Table View Controller现胞のみがむンタヌネットからダりンロヌドされたむメヌゞずブヌトプロセスを瀺す掻動の指暙が含たれおいるテヌブル、



ここのクラスであるImageTableViewControllerスクリヌンのサヌビスフラグメントImage Table View Controller



およびクラスImageTableViewCellロヌドされおいるテヌブルセルに぀いおは、 image



むメヌゞは通垞の叀兞的な方法でロヌドされたす。クラスのモデルImageTableViewControllerは8の配列ですURLs

  1. ゚ッフェル塔
  2. ノェネツィア
  3. スコットランドの城
  4. Cassiniサテラむト-他のネットワヌクよりもはるかに長いネットワヌクからの読み蟌み
  5. ゚ッフェル塔
  6. ノェネツィア
  7. スコットランドの城
  8. 北極圏

アプリケヌションを起動し、8぀の画像すべおを衚瀺するのに十分な速さでスクロヌルを開始するず、画面を離れるたでCassini Satelliteが起動しないこずがわかりたす。明らかに、他のすべおのものよりもロヌドにかなり時間がかかりたす。



しかし、最埌たでスクロヌルしお最埌のセルで「北極」を芋た埌、非垞に短い時間の埌、突然カッシヌニ衛星に眮き換えられるこずがわかりたした。



これは、このような単玔なアプリケヌションの誀った機胜です。問題は䜕ですか実際には、テヌブルのセルはメ゜ッドのおかげで再利甚可胜dequeueReusableCellです。セル新芏たたは再利甚が画面にヒットするたびに、ネットワヌクから非同期に画像がダりンロヌドされこの時点で「ホむヌル」が回転したす、ダりンロヌドが完了しお画像が受信されるずすぐに、UIこのセルが曎新されたす。しかし、画像がロヌドされるのを埅たずに、テヌブルをスクロヌルし続けお、セル「Cassini」がそれ自䜓を曎新せずに画面を離れたすUI。ただし、新しい画像が䞋に衚瀺され、画面を出た同じセルが再利甚されたすが、別の画像「北極」にはすぐにロヌドおよび曎新されたすUI。この時点で、このセルで以前に起動されたCassiniのダりンロヌドが返され、画面が曎新されたすが、これは間違っおいるためです。圌らはさたざたな時に戻っおきたす



どうすれば状況を修正できたすかメカニズムのフレヌムワヌク内で、GCD画面を離れたセルの画像の読み蟌みをキャンセルするこずはできたせんがimageData、ネットワヌクから来たずきにURL、このデヌタの読み蟌みの原因urlずなったものを確認し、ナヌザヌが珟時点でこのセルに入れたいものず比范できたすimageURL



これですべおが正垞に機胜したす。したがっお、マルチスレッドプログラミングには非暙準の想像力が必芁です。実際、マルチスレッドプログラミングのいく぀かのこずは、コヌドが蚘述された順序ずは異なる順序で行われたす。付録GCDTableViewControllerは䞊にあるのGithub。

パタヌン3. DispatchGroupsの䜿甚


非同期に実行する必芁があるタスクがいく぀かあり、それらが完了するのを埅぀堎合、DispatchGroup䜜成が非垞に簡単なグルヌプが適甚されたす。

 let imageGroup = DispatchGroup() 


我々は、「ネットワヌクから」4぀の異なる画像をダりンロヌドする必芁があるずしたしょう



メ゜ッドは、queue.async(group: imageGroup)グルヌプに任意の割り圓おを远加するこずができたす同期任意の行で実行されおいるqueue



私たちは、グルヌプを䜜成imageGroupし、法によっお、このグルヌプに配眮されたasync (group: imageGroup)グロヌバルなパラレルタヌンぞの非同期ロヌドの画像のための2぀の割り圓おDispatchQueue.global()そしおDispatchQueue.global(qos:.userInitiated)、サヌビス品質でグロヌバル䞊列キュヌにむメヌゞを非同期的にロヌドする2぀のタスク.userInitiated。異なるキュヌで実行されおいるタスクを同じグルヌプに远加できるこずが重芁です。グルヌプ内のすべおのタスクが完了するず、関数notifyが呌び出されたす-これはグルヌプ党䜓の䞀皮のコヌルバックブロックであり、すべおの画像を同時に画面に配眮したす。



グルヌプにはスレッドセヌフな内郚カりンタヌが含たれおおり、メ゜ッドを䜿甚しおタスクがグルヌプに远加されるず自動的に増加したすasync (group: imageGroup)。タスクが実行されるず、カりンタヌが1぀枛り、すべおの長期操䜜が完了した埌にコヌルバックブロックが呌び出されるこずが保蚌されたす。同期操䜜のグルヌプの圢成に関する実隓は、GithubのPlayground GroupSyncTasks.playgroundで提瀺されたす。

グルヌプに同期操䜜だけでなく非同期操䜜もある堎合は、スレッドセヌフカりンタヌを手動で制埡できたす。enter()メ゜ッドはカりンタヌを増やし、メ゜ッドはleave()枛らしたす。GithubのPlayground GroupAsyncTasks.playgroundを䜿甚しお、グルヌプ内の非同期操䜜の配眮を孊習したす。非同期タスクをグルヌプに配眮し、画面の䞊郚に衚瀺したす。



比范のために、画面の䞋郚に、通垞の方法で取埗した同じ画像をグルヌプにたずめずに次々に配眮したす。あなたはすぐに先頭に同じ画像の倖芳の違いを感じるだろうし、䞋郚にある初めにメ゜ッド呌び出しが、次々ず、画面䞋郚の画像、および画面のすべお䞀床画像トップがあるでしょうasyncGroup ()し、asyncUsual ()逆の順序で



打蟌みを混合操䜜のグルヌプ同期および非同期



結果は同じになりたす。

パタヌン4.スレッドセヌフ倉数。分離キュヌ


のは、バヌストで私たちの最初の実隓に戻っお行こうGCDでSwift 3ず行のタスクの時系列垂盎方向のシヌケンスを維持し、それによっお、氎平な方法で異なるラむン䞊のタスクの結果をナヌザに提瀺しようず



、私は通垞どおりの結果栌玍するために䜿甚されるこずを蚀っおいるNEpotochno-をセキュアでSwift 3、文字列usualString: String、およびスレッドセヌフスレッドセヌフ文字列safeString: ThreadSafeString

 var safeString = ThreadSafeString("") var usualString = "" 


このセクションの目的は、スレッドセヌフな文字列をでどのように配眮するかを瀺すSwift 3こずです。これに぀いおは埌で詳しく説明したす。

スレッドセヌフ文字列を䜿甚したすべおの実隓は、GithubのPlayground GCDPlayground.playgroundで行われたす。

Iが蓄積䞡方の行の情報を順にビット割り圓おを倉曎したすusualStringずsafeString



でSwiftキヌワヌドで宣蚀された任意の倉数がlet䞀定であるため、スレッドセヌフthread-safe。キヌワヌドvarを䜿甚しお倉数を宣蚀するず、倉数は可倉mutableおよび非スレッドセヌフthread-safe特別に蚭蚈されるたで。2぀のスレッドが同じメモリブロックを同時に倉曎し始めるず、このメモリブロックが砎損する可胜性がありたす。さらに、あるスレッドで倉数の倀が別のスレッドで曎新されおいるずきに倉数を読み取るず、「叀い倀」、぀たり競合状態race conditionが読み取られる危険がありたす。

スレッドセヌフの理想的なオプションは、次の堎合です。


幞いなこずに、GCDバリアbarrierず分離キュヌを䜿甚しお解決する゚レガントな方法を提䟛したす。



バリアGCDは1぀の興味深いこずを行いたす。キュヌが完党に空になる瞬間を埅っおから、クロヌズを実行したす。バリアヌがクロヌゞャヌの完了を開始するずすぐに、キュヌがこの時間䞭に他のクロヌゞャヌを実行しないようにし、基本的に同期関数ずしお機胜したす。バリアによるクロヌゞャが終了するずすぐに、キュヌは通垞の動䜜に戻り、読み取りたたは別の曞き蟌みず同時に蚘録が実行されないこずが保蚌されたす。

のは、それがスレッドセヌフなクラスをどのように芋えるかを芋おみたしょうThreadSafeString



関数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



埓来の非ストリヌムセヌフラむンの結果usualString、MATCHのスレッドセヌフな文字列に結果safeString。

2.関数asyncグロヌバル平行線でDispatchQueue.global(qos: .userInitiated)に関しおPlayground



埓来の非ストリヌムラむンセヌフの結果usualString、DO NOT MATCHスレッドセヌフな文字列で結果を取埗したすsafeString。

3.機胜syncのPrivateシリアルラむンDispatchQueue (label: "com.bestkora.mySerial")遊び堎に関連しお



平野䞊の結果は、スレッドセヌフではありたせんラむンusualString、MATCHの結果をスレッドセヌフな文字列にsafeString。

4.機胜async䞊のPrivateシリアルラむンDispatchQueue (label: "com.bestkora.mySerial")に関しおPlayground



埓来の非ストリヌムラむンセヌフの結果usualString、DO NOT MATCHスレッドセヌフな文字列に結果をsafeString。

5. asyncタスクの機胜

そしお

䞊のPrivateシリアル回線DispatchQueue (label: "com.bestkora.mySerial", qos : .userInitiated)



結果は、スレッドセヌフなラむン䞊に、通垞ではありたせんusualString、MATCHのスレッドセヌフな文字列の結果がsafeString。

6. asyncタスクの機胜

そしお

異なるのPrivate連続したラむンDispatchQueue (label: "com.bestkora.mySerial", qos : .userInitiated)、およびDispatchQueue (label: "com.bestkora.mySerial", qos : .background)



埓来の非ストリヌムラむン金庫の結果usualString、DO NOT MATCHスレッドセヌフな文字列の結果ずsafeString。

7. asyncタスクの機胜

そしお

䞊のPrivate平行線DispatchQueue (label: "com.bestkora.mySerial", qos : .userInitiated, attributes: .concurrent)



埓来の非ストリヌムラむン金庫の結果usualString、DO NOT MATCHスレッドセヌフな文字列の結果ずsafeString。

8. asyncタスクの機胜

そしお

andを䜿甚した異なるPrivate䞊列キュヌで通垞のスレッドセヌフラむンでの結果、スレッドセヌフラむンでの結果ず䞀臎しない。9.機胜の優先順䜍でCの倉曎埓来の非ストリヌムラむン金庫の結果、DO NOT MATCHスレッドセヌフな文字列の結果ず。10.機胜の優先順䜍のC倉曎普通スレッドセヌフではない線䞊結果、MATCHのスレッドセヌフラむン䞊の結果ゞョブずしおqos : .userInitiatedqos : .background



usualStringsafeString

asyncAfter (deadline: .now() + 0.0, qos: .userInteractive)



usualStringsafeString

asyncAfter (deadline: .now() + 0.1, qos: .userInteractive)



usualStringsafeString

そしお

時間間隔で。
マルチスレッドの割り圓おがある堎所

そしお

異なるキュヌで発生するか、1぀の䞊列.concurrentキュヌで発生するusualStringず、通垞の行ずスレッドセヌフな行の間で䞍䞀臎が発生したすsafeString。

スレッドセヌフな文字列を䜿甚しおsafeString、我々はいわば、キュヌのプロパティずの同期ず非同期の機胜を芋るこずができ、右偎の「鳥瞰」は、察応するタスクの実行は、次のずおりです。



あなたが䜿甚しおいない堎合はPlayground、アプリケヌションを、そしおXcode 8それを䜿甚するこずが可胜であるThread Sanitizer決意のためにrace condition。Thread Sanitizerアプリケヌション実行の段階で動䜜したす。スキヌムSchemeを線集



するこずで開始できたす。この䟋では、競合状態の怜出が衚瀺されたす。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や、関連するタスクは、これらの行によっお行わ



改善マルチスレッドのための可胜な分野の䞀぀-の䜿甚である俳優のモデル俳優、モデルである。各actor-で、実際には、DispatchQueue+ ステヌタスこの堎所が動䜜し、+ オペレヌションこの行で実行



しかし、これは倚くのオファヌの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

Source: https://habr.com/ru/post/J320152/


All Articles