Concurrency¶ 並行性¶
Swift has built-in support for writing asynchronous and parallel code in a structured way. Asynchronous code can be suspended and resumed later, although only one piece of the program executes at a time. Suspending and resuming code in your program lets it continue to make progress on short-term operations like updating its UI while continuing to work on long-running operations like fetching data over the network or parsing files. Parallel code means multiple pieces of code run simultaneously—for example, a computer with a four-core processor can run four pieces of code at the same time, with each core carrying out one of the tasks. A program that uses parallel and asynchronous code carries out multiple operations at a time; it suspends operations that are waiting for an external system, and makes it easier to write this code in a memory-safe way. スウィフトは、非同期のそして並列なコードをある構造化された方法で書くための組込みのサポートを持ちます。非同期コードは、中断されて後で再開されることが可能です、とはいえプログラムの1つの断片だけは一度に遂行します。コードをあなたのプログラムにおいて中断および再開することは、それに、データをネットワーク越しに取ってくるまたはファイルを構文解析するといった長く続く演算に取り組み続けている間に、それのUIを更新するといった短期間の演算の進捗を継続させます。並列コードは、複数のコード断片が同時に動作することを意味します — 例えば、4コアプロセッサをもつコンピュータは4つのコード断片を、各コアがそれらタスクの1つを実施することで一度に実行できます。並列および非同期のコードを使用するプログラムは、複数の演算を一度に実施します;それは、外部システムを待つ演算を中断します、そしてこのコードをメモリ安全な方法で書くのをより簡単にします。
The additional scheduling flexibility from parallel or asynchronous code also comes with a cost of increased complexity. Swift lets you express your intent in a way that enables some compile-time checking—for example, you can use actors to safely access mutable state. However, adding concurrency to slow or buggy code isn’t a guarantee that it will become fast or correct. In fact, adding concurrency might even make your code harder to debug. However, using Swift’s language-level support for concurrency in code that needs to be concurrent means Swift can help you catch problems at compile time. 並列または非同期のコードによって加味されたスケジュール的柔軟性はまた、複雑さの増加という犠牲を伴います。スウィフトは、あなたにあなたの意図をある方法、何らかのコンバイル時での検査を可能にするもので表現させます — 例えば、あなたはアクターを使用して、可変な状態に安全にアクセスできます。しかしながら、並行性を低速またはバグのあるコードに加えることは、それが高速または正しくなることを保証しません。実際に、並行性を加えることは、あなたのコードをデバッグするのを難しくしさえするかもしれません。しかしながら、並行性に対するスウィフトのもつ言語水準サポートを使うことは、並行である(共同して働く、同時に起こる)ことを必要とするコードにおいて、あなたがコンパイル時に問題を捕まえるのをスウィフトが助け得るのを意味します。
The rest of this chapter uses the term concurrency to refer to this common combination of asynchronous and parallel code. この章の残りは、非同期および並列コードのこの普通の組み合わせに言及するのに用語、並行性を用います。
Note 注意
If you’ve written concurrent code before, you might be used to working with threads. The concurrency model in Swift is built on top of threads, but you don’t interact with them directly. An asynchronous function in Swift can give up the thread that it’s running on, which lets another asynchronous function run on that thread while the first function is blocked. あなたが並行性コードを前に書いたことがあるならば、あなたはスレッドを扱うことに慣れているかもしれません。スウィフトにおける並行性モデルはスレッドそれらの最上部に構築されます、しかしあなたはそれらと直接に相互作用しません。スウィフトにおける非同期関数は、それがその上で動作しているところのスレッドを諦めることができます、それは別の非同期関数にそのスレッド上で動作させます、一方で最初の関数はブロック(遮断)されます。
Although it’s possible to write concurrent code without using Swift’s language support, that code tends to be harder to read. For example, the following code downloads a list of photo names, downloads the first photo in that list, and shows that photo to the user: 並行性コードをスウィフトのもつ言語サポートを使うことなく書くことは可能であるとはいえ、そのコードは読むのがより困難になりがちです。例えば、以下のコードは写真名のリストをダウンロードして、そのリストの最初の写真をダウンロードして、そしてその写真をユーザに示します:
- listPhotos(inGallery: "Summer Vacation") { photoNames in
- let sortedNames = photoNames.sorted()
- let name = sortedNames[0]
- downloadPhoto(named: name) { photo in
- show(photo)
- }
- }
Even in this simple case, because the code has to be written as a series of completion handlers, you end up writing nested closures. In this style, more complex code with deep nesting can quickly become unwieldy. この単純な事例においてさえ、コードが一連の完了ハンドラとして書かれなければならないことから、あなたは最終的に複数のクロージャを入れ子にして書くことになります。このやり方では、深く入れ子になっているさらに複雑なコードは、すぐに手に負えなくなりえます。
Defining and Calling Asynchronous Functions¶ 非同期関数の定義と呼び出し¶
An asynchronous function or asynchronous method is a special kind of function or method that can be suspended while it’s partway through execution. This is in contrast to ordinary, synchronous functions and methods, which either run to completion, throw an error, or never return. An asynchronous function or method still does one of those three things, but it can also pause in the middle when it’s waiting for something. Inside the body of an asynchronous function or method, you mark each of these places where execution can be suspended. 非同期関数または非同期メソッドは、関数またはメソッドのある特別な種類です、それはそれが遂行の途中である間に中断されることが可能です。これは、普通の同期関数およびメソッドとは対照的です、それは完了するまで実行する、エラーをスローする、または決して復帰しないのどれかです。非同期の関数やメソッドは依然としてそれら3つのことの1つを行います、しかしそれはまた、それが何かを待っている時に途中で一時停止できます。非同期の関数やメソッドの本体の内部で、あなたは遂行が中断されることが可能であるそれら場所のそれぞれに印をつけます。
To indicate that a function or method is asynchronous, you write the async
keyword in its declaration after its parameters, similar to how you use throws
to mark a throwing function. If the function or method returns a value, you write async
before the return arrow (->
). For example, here’s how you might fetch the names of photos in a gallery:
ある関数またはメソッドが非同期であることを指し示すために、あなたはasync
キーワードをそれの宣言の中でそれのパラメータの後に書きます、あなたがthrows
を使ってスロー関数を印する方法と同じように。関数またはメソッドが値を返すならば、あなたはasync
をその戻り矢印(->
)の前に書きます。例えば、ここにあなたがあるギャラリーの写真の名前を取って来るかもしれない方法があります:
- func listPhotos(inGallery name: String) async -> [String] {
- let result = // ... some asynchronous networking code ...
- return result
- }
For a function or method that’s both asynchronous and throwing, you write async
before throws
.
非同期とスローの両方である関数やメソッドに対して、あなたはasync
をthrows
の前に書きます。
When calling an asynchronous method, execution suspends until that method returns. You write await
in front of the call to mark the possible suspension point. This is like writing try
when calling a throwing function, to mark the possible change to the program’s flow if there’s an error. Inside an asynchronous method, the flow of execution is suspended only when you call another asynchronous method—suspension is never implicit or preemptive—which means every possible suspension point is marked with await
.
非同期メソッドを呼び出す時、遂行はそのメソッドが戻るまで中断されます。あなたは、await
をその呼び出しの前に書くことで可能な中断地点を印します。これは、エラーがある場合のプログラムの流れに対する可能な変更を印するために、try
をスロー関数の呼び出しの時に書くことに似ています。非同期メソッドの内部では、遂行の流れは、あなたが別の非同期メソッドを呼び出す時にのみ中断されます — 中断は決して暗黙的または先制的ではありません — それが意味するのはあらゆる可能な中断地点はawait
で印されるということです。
For example, the code below fetches the names of all the pictures in a gallery and then shows the first picture: 例えば、下のコードは、あるギャラリーの全ての画像の名前を取ってきて、それから最初の画像を示します:
- let photoNames = await listPhotos(inGallery: "Summer Vacation")
- let sortedNames = photoNames.sorted()
- let name = sortedNames[0]
- let photo = await downloadPhoto(named: name)
- show(photo)
Because the listPhotos(inGallery:)
and downloadPhoto(named:)
functions both need to make network requests, they could take a relatively long time to complete. Making them both asynchronous by writing async
before the return arrow lets the rest of the app’s code keep running while this code waits for the picture to be ready.
listPhotos(inGallery:)
およびdownloadPhoto(named:)
関数の両方がネットワーク要請をする必要があることから、それらは完了するのに比較的に長い時間がかかる可能性があります。それらを両方ともasync
を戻り矢印の前に書くことによって非同期にすることは、アプリのもつコードの残りに動作を続けさせます、一方でこのコードは画像が利用可能になるのを待ちます。
To understand the concurrent nature of the example above, here’s one possible order of execution: 上の例の並行の本質を理解するために、ここに遂行の1つの起こりうる順番があります:
- The code starts running from the first line and runs up to the first
await
. It calls thelistPhotos(inGallery:)
function and suspends execution while it waits for that function to return. コードは、最初の行から動作を開始します、そして最初のawait
に至るまで動作します。それはlistPhotos(inGallery:)
関数を呼び出します、そしてその関数が戻るのをそれが待つあいだ遂行を中断します。 - While this code’s execution is suspended, some other concurrent code in the same program runs. For example, maybe a long-running background task continues updating a list of new photo galleries. That code also runs until the next suspension point, marked by
await
, or until it completes. このコードの遂行が中断される間、何らかの他の並行性コードは同じプログラムの中で動作します。例えば、たぶん長期動作のバックグラウンドタスクは、新しい写真ギャラリーのリストの更新を続けます。そのコードはまた、await
によって印される次の中断地点まで、またはそれが完了するまで動作します。 - After
listPhotos(inGallery:)
returns, this code continues execution starting at that point. It assigns the value that was returned tophotoNames
.listPhotos(inGallery:)
が返った後、このコードはその地点で開始して遂行を続けます。それは、返された値をphotoNames
に代入します。 - The lines that define
sortedNames
andname
are regular, synchronous code. Because nothing is markedawait
on these lines, there aren’t any possible suspension points.sortedNames
とname
を定義する行は、普通の、同期コードです。まったくawait
で印されるものがこれらの行にないので、可能な中断地点は何もありません。 - The next
await
marks the call to thedownloadPhoto(named:)
function. This code pauses execution again until that function returns, giving other concurrent code an opportunity to run. 次のawait
は、downloadPhoto(named:)
関数への呼び出しを印します。このコードは、その関数が返るまで再び遂行を一時停止します、他の並行性コードに動作する機会を与えて。 - After
downloadPhoto(named:)
returns, its return value is assigned tophoto
and then passed as an argument when callingshow(_:)
.downloadPhoto(named:)
が返った後、それの戻り値はphoto
に割り当てられます、そしてそれから引数としてshow(_:)
を呼び出す時に渡されます。
The possible suspension points in your code marked with await
indicate that the current piece of code might pause execution while waiting for the asynchronous function or method to return. This is also called yielding the thread because, behind the scenes, Swift suspends the execution of your code on the current thread and runs some other code on that thread instead. Because code with await
needs to be able to suspend execution, only certain places in your program can call asynchronous functions or methods:
あなたのコードの中のawait
で印されるそれら可能な中断地点が指し示すのは、コードの現在の断片が遂行を非同期の関数やメソッドが返るのを待っている間に一時停止するかもしれない、ということです。これはまた、スレッドの譲渡とも呼ばれます、なぜなら、シーンそれらの背後で、スウィフトはあなたのコードの遂行を現在のスレッド上で中断して、何らかの他のコードをそのスレッド上で代わりに実行するからです。await
をもつコードは遂行を中断できる必要があることから、あなたのプログラムの中の特定の部分だけが非同期の関数やメソッドを呼び出すことが可能です:
- Code in the body of an asynchronous function, method, or property. 非同期の関数、メソッド、またはプロパティの本体の中のコード。
- Code in the static
main()
method of a structure, class, or enumeration that’s marked with@main
.@main
で印される構造体、クラス、または列挙の静的main()
メソッドの中のコード。 - Code in an unstructured child task, as shown in Unstructured Concurrency below. 下の構造化されない並行性の中で示されるような、ある構造化されない子タスクの中のコード。
Note 注意
The Task.sleep(nanoseconds:)
method is useful when writing simple code to learn how concurrency works. This method does nothing, but waits at least the given number of nanoseconds before it returns. Here’s a version of the listPhotos(inGallery:)
function that uses sleep(nanoseconds:)
to simulate waiting for a network operation:
Task.sleep(nanoseconds:)
メソッドは、単純なコードを書いてどのように並行性が働くかを学ぶのに役立ちます。このメソッドは何もしません、しかし少なくとも与えられたナノ秒数をそれが返る前に待ちます。ここにlistPhotos(inGallery:)
関数のあるバージョンがあります、それはsleep(nanoseconds:)
を使ってネットワーク演算を待つことの模擬実験をします:
- func listPhotos(inGallery name: String) async throws -> [String] {
- try await Task.sleep(nanoseconds: 2 * 1_000_000_000) // Two seconds
- return ["IMG001", "IMG99", "IMG0404"]
- }
Asynchronous Sequences¶ 非同期シーケンス¶
The listPhotos(inGallery:)
function in the previous section asynchronously returns the whole array at once, after all of the array’s elements are ready. Another approach is to wait for one element of the collection at a time using an asynchronous sequence. Here’s what iterating over an asynchronous sequence looks like:
前の節でのlistPhotos(inGallery:)
関数は、配列の持つ要素の全てが準備できた後に、配列全体を非同期に一度に返します。別の取り組み方は、非同期シーケンスを使用して一度にコレクションの1つの要素に対して待機することです。ここに、ある非同期シーケンスのすべてにわたって反復することがどのように見えるかがあります:
- import Foundation
- let handle = FileHandle.standardInput
- for try await line in handle.bytes.lines {
- print(line)
- }
Instead of using an ordinary for
-in
loop, the example above writes for
with await
after it. Like when you call an asynchronous function or method, writing await
indicates a possible suspension point. A for
-await
-in
loop potentially suspends execution at the beginning of each iteration, when it’s waiting for the next element to be available.
普通のfor
-in
ループを使う代わりに、上の例はfor
をそれの後のawait
と共に書きます。あなたが非同期関数やメソッドを呼び出すときのように、await
を書くことは可能な中断地点を指し示します。for
-await
-in
ループは、潜在的に遂行を各反復の始まりで中断します、次の要素が利用可能になるのをそれが待っている場合は。
In the same way that you can use your own types in a for
-in
loop by adding conformance to the Sequence
protocol, you can use your own types in a for
-await
-in
loop by adding conformance to the AsyncSequence
protocol.
あなたがあなた独自の型をfor
-in
ループにおいてSequence
プロトコルへの準拠を加えることによって使うのと同じ方法で、あなたはあなた独自の型をfor
-await
-in
ループにおいて使うことがAsyncSequence
プロトコルへの準拠を加えることによって可能です。
Calling Asynchronous Functions in Parallel¶ 非同期関数を並列に呼び出す¶
Calling an asynchronous function with await
runs only one piece of code at a time. While the asynchronous code is running, the caller waits for that code to finish before moving on to run the next line of code. For example, to fetch the first three photos from a gallery, you could await three calls to the downloadPhoto(named:)
function as follows:
非同期関数をawait
とともに呼び出すことは、ただコードの1断片を一度実行するだけです。ある非同期コードが動作している間、呼び出し側は、コードの次の行を実行するために移動する前に、そのコードが終了するのを待ちます。例えば、最初の3枚の写真をあるギャラリーから取ってくるには、あなたはdownloadPhoto(named:)
関数への3つの呼び出しに対して待機します、次のように:
- let firstPhoto = await downloadPhoto(named: photoNames[0])
- let secondPhoto = await downloadPhoto(named: photoNames[1])
- let thirdPhoto = await downloadPhoto(named: photoNames[2])
- let photos = [firstPhoto, secondPhoto, thirdPhoto]
- show(photos)
This approach has an important drawback: Although the download is asynchronous and lets other work happen while it progresses, only one call to downloadPhoto(named:)
runs at a time. Each photo downloads completely before the next one starts downloading. However, there’s no need for these operations to wait—each photo can download independently, or even at the same time.
この取り組みはある重要な欠点があります:ダウンロードが非同期でありそれが進捗する間に他の仕事が起こるに任せるとはいえ、ただ1つのdownloadPhoto(named:)
への呼び出しだけが一度に動作します。各写真は、次のものがダウンロードを開始する前に、完全にダウンロードされます。しかしながら、それら演算が待機する必要はありません — 各写真は独立して、または同時にさえダウンロード可能です。
To call an asynchronous function and let it run in parallel with code around it, write async
in front of let
when you define a constant, and then write await
each time you use the constant.
非同期関数を呼び出してそしてそれをそのまわりのコードと並列に動作させるには、あなたが定数を定義する時にasync
をlet
の前に書いてください、それからあなたがその定数を使うたびごとにawait
を書いてください。
- async let firstPhoto = downloadPhoto(named: photoNames[0])
- async let secondPhoto = downloadPhoto(named: photoNames[1])
- async let thirdPhoto = downloadPhoto(named: photoNames[2])
- let photos = await [firstPhoto, secondPhoto, thirdPhoto]
- show(photos)
In this example, all three calls to downloadPhoto(named:)
start without waiting for the previous one to complete. If there are enough system resources available, they can run at the same time. None of these function calls are marked with await
because the code doesn’t suspend to wait for the function’s result. Instead, execution continues until the line where photos
is defined—at that point, the program needs the results from these asynchronous calls, so you write await
to pause execution until all three photos finish downloading.
この例において、downloadPhoto(named:)
への3つの呼び出し全ては、前のものが完了するのを待つことなしに開始します。十分なシステムリソースが利用可能ならば、それらは同時に動作可能です。これらの関数呼び出しのどれひとつとしてawait
で印されません、なぜならコードは関数の結果を待つために中断しないからです。代わりに、遂行はphotos
が定義されるところの行まで継続します — その時点で、プログラムはそれら非同期呼び出しからの結果を必要とします、なのであなたはawait
を書いて、3枚の写真全てがダウンロードを終了するまで遂行を中断します。
Here’s how you can think about the differences between these two approaches: ここに、あなたがこれら2つの取り組みの間の違いについてどのように考えることが可能かがあります:
- Call asynchronous functions with
await
when the code on the following lines depends on that function’s result. This creates work that is carried out sequentially. 非同期関数をawait
と共に呼び出してください、続く行でのコードがその関数の結果に依存する場合は。これは、逐次的に実施される仕事を作成します。 - Call asynchronous functions with
async
-let
when you don’t need the result until later in your code. This creates work that can be carried out in parallel. 非同期関数をasync
-let
と共に呼び出してください、あなたが結果をあなたのコードにおいてもっと後まで必要としない場合は。これは、並列に実施されることができる仕事を作成します。 - Both
await
andasync
-let
allow other code to run while they’re suspended.await
とasync
-let
の両方は、それらが中断される間に他のコードが動作するのを許可します。 - In both cases, you mark the possible suspension point with
await
to indicate that execution will pause, if needed, until an asynchronous function has returned. 両方の場合において、あなたは可能な中断地点をawait
で印することでその遂行が一時停止することを指し示します、必要ならば、非同期関数が返ってしまうまで。
You can also mix both of these approaches in the same code. あなたはまた、これら取り組みの両方を同じコードにおいて混ぜることができます。
Tasks and Task Groups¶ タスクとタスクグループ¶
A task is a unit of work that can be run asynchronously as part of your program. All asynchronous code runs as part of some task. The async
-let
syntax described in the previous section creates a child task for you. You can also create a task group and add child tasks to that group, which gives you more control over priority and cancellation, and lets you create a dynamic number of tasks.
タスクは、ひとつの仕事の単位です、それは非同期にあなたのプログラムの一部として実行されることが可能なものです。全ての非同期コードは、あるタスクの一部として動作します。前の節で記述されるasync
-let
構文は、子タスクをあなたの代わりに作成します。あなたはまた、タスクグループを作成して、子タスクそれらをそのグループに加えることができます、それはあなたに優先と取消に対するさらなる制御を与えます、そしてあなたに動的な数のタスクを作成させます。
Tasks are arranged in a hierarchy. Each task in a task group has the same parent task, and each task can have child tasks. Because of the explicit relationship between tasks and task groups, this approach is called structured concurrency. Although you take on some of the responsibility for correctness, the explicit parent-child relationships between tasks lets Swift handle some behaviors like propagating cancellation for you, and lets Swift detect some errors at compile time. タスクは、ある階層の中にきちんと並べられます。タスクグループの中の各タスクは、同じ親タスクを持ちます、そして各タスクは子タスクいくつかを持つことができます。タスクとタスクグループの間のこの明確な関係のために、この取り組みは構造化された並行性と呼ばれます。あなたが正しさに対する責任の一部を負うとはいえ、タスク間の明確な親子関係は、取り消しをあなたに代わって伝播することのようないくつかの挙動をスウィフトに取り扱わせます。
- await withTaskGroup(of: Data.self) { taskGroup in
- let photoNames = await listPhotos(inGallery: "Summer Vacation")
- for name in photoNames {
- taskGroup.addTask { await downloadPhoto(named: name) }
- }
- }
For more information about task groups, see TaskGroup
.
タスクグループについての更なる情報として、TaskGroup
を見てください。
Unstructured Concurrency¶ 構造化されない並行性¶
In addition to the structured approaches to concurrency described in the previous sections, Swift also supports unstructured concurrency. Unlike tasks that are part of a task group, an unstructured task doesn’t have a parent task. You have complete flexibility to manage unstructured tasks in whatever way your program needs, but you’re also completely responsible for their correctness. To create an unstructured task that runs on the current actor, call the Task.init(priority:operation:)
initializer. To create an unstructured task that’s not part of the current actor, known more specifically as a detached task, call the Task.detached(priority:operation:)
class method. Both of these operations return a task handle that lets you interact with the task—for example, to wait for its result or to cancel it.
前の節で記述される並行性に対する構造化された取り組みに加えて、スウィフトはまた構造化されない並行性をサポートします。あるタスクグループの一部であるタスクそれらとは違って、非構造化タスクは親タスクを持ちません。あなたは、なんでもあなたのプログラムが必要とする方法において非構造化タスクを管理する完全な柔軟性を持ちます、しかしあなたはまたそれらの正しさに対して完全に責任を負います。現在のアクター上で動作するある非構造化タスクを作成するために、Task.init(priority:operation:)
イニシャライザを呼び出してください。現在のアクターの一部でない非構造化タスク、より具体的に分離されたタスクとして知られるもの、を作成するには、Task.detached(priority:operation:)
クラスメソッドを呼び出してください。これら演算の両方ともあるタスクハンドルを返します、それはあなたにタスクと相互作用させるものです — 例えば、それの結果を待つためまたはそれを取り消すために。
- let newPhoto = // ... some photo data ...
- let handle = Task {
- return await add(newPhoto, toGalleryNamed: "Spring Adventures")
- }
- let result = await handle.value
For more information about managing detached tasks, see Task
.
分離されたタスクを管理することについての更なる情報として、Task
を見てください。
Task Cancellation¶ タスク取り消し¶
Swift concurrency uses a cooperative cancellation model. Each task checks whether it has been canceled at the appropriate points in its execution, and responds to cancellation in whatever way is appropriate. Depending on the work you’re doing, that usually means one of the following: スウィフト並行性は、協調取り消しモデルを使います。各タスクは、それがそれの遂行において適切な地点で取り消されたかどうか確認します、そしてなんでも適切な方法で取り消しに応答します。あなたが行っている作業に依存して、それは通常は以下の1つを意味します:
- Throwing an error like
CancellationError
CancellationError
に似たエラーをスローする - Returning
nil
or an empty collectionnil
または空のコレクションを返す - Returning the partially completed work 部分的に完了した作業を返す
To check for cancellation, either call Task.checkCancellation()
, which throws CancellationError
if the task has been canceled, or check the value of Task.isCancelled
and handle the cancellation in your own code. For example, a task that’s downloading photos from a gallery might need to delete partial downloads and close network connections.
取り消しを検査するには、Task.checkCancellation()
を呼び出すか、それはCancellationError
をそのタスクが取り消されたならばスローします、またはTask.isCancelled
の値を調べて取り消しをあなた独自のコードにおいて取り扱うか、どちらかをしてください。例えば、写真をギャラリーからダウンロードするタスクは、中途半端なダウンロードを削除してネットワーク接続を閉じる必要があるかもしれません。
To propagate cancellation manually, call Task.cancel()
.
取り消しを手動で伝播するには、Task.cancel()
を呼び出してください。
Actors¶ アクター¶
Like classes, actors are reference types, so the comparison of value types and reference types in Classes Are Reference Types applies to actors as well as classes. Unlike classes, actors allow only one task to access their mutable state at a time, which makes it safe for code in multiple tasks to interact with the same instance of an actor. For example, here’s an actor that records temperatures: クラスのように、アクターは参照型です、なのでクラスは参照型ですにおける値型と参照型の比較がクラスと同様にアクターに適用されます。クラスと異なり、アクターそれらは一度にただ1つのタスクにだけそれらの可変状態へのアクセスを許可します、それはコードにとって複数タスクにおいてあるアクターの同じインスタンスと相互作用することを安全なものにします。例えば、ここに温度を記録するあるアクターがあります:
- actor TemperatureLogger {
- let label: String
- var measurements: [Int]
- private(set) var max: Int
- init(label: String, measurement: Int) {
- self.label = label
- self.measurements = [measurement]
- self.max = measurement
- }
- }
You introduce an actor with the actor
keyword, followed by its definition in a pair of braces. The TemperatureLogger
actor has properties that other code outside the actor can access, and restricts the max
property so only code inside the actor can update the maximum value.
あなたはあるアクターをactor
キーワード、それに続けて一対の波カッコの中のそれの定義で導入します。TemperatureLogger
アクターはプロパティいくつかを持ちます、それらはそのアクターの外側の他のコードがアクセスできます、そしてmax
プロパティを制限します、それでアクター内部のコードだけが最大値を更新することが可能です。
You create an instance of an actor using the same initializer syntax as structures and classes. When you access a property or method of an actor, you use await
to mark the potential suspension point—for example:
あなたは、アクターのインスタンスを構造体およびクラスと同じ初期化構文を使って作成します。あなたがアクターのプロパティまたはメソッドにアクセスする時、あなたはawait
を使うことで潜在的な中断地点を印します — 例えば:
- let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
- print(await logger.max)
- // Prints "25"
In this example, accessing logger.max
is a possible suspension point. Because the actor allows only one task at a time to access its mutable state, if code from another task is already interacting with the logger, this code suspends while it waits to access the property.
この例において、logger.max
へのアクセスは、ある可能な中断地点です。アクターが一度に1つのタスクだけにそれの可変状態へのアクセスを許可することから、別のタスクからのコードが既にloggerと相互作用しているならば、このコードはそれがプロパティにアクセスするために待つ間ずっと中断します。
In contrast, code that’s part of the actor doesn’t write await
when accessing the actor’s properties. For example, here’s a method that updates a TemperatureLogger
with a new temperature:
対照的に、アクターの一部であるコードは、await
をそのアクターのもつプロパティにアクセスする時に書きません。例えば、ここにあるメソッドがあります、それはTemperatureLogger
を新しい温度で更新します:
- extension TemperatureLogger {
- func update(with measurement: Int) {
- measurements.append(measurement)
- if measurement > max {
- max = measurement
- }
- }
- }
The update(with:)
method is already running on the actor, so it doesn’t mark its access to properties like max
with await
. This method also shows one of the reasons why actors allow only one task at a time to interact with their mutable state: Some updates to an actor’s state temporarily break invariants. The TemperatureLogger
actor keeps track of a list of temperatures and a maximum temperature, and it updates the maximum temperature when you record a new measurement. In the middle of an update, after appending the new measurement but before updating max
, the temperature logger is in a temporary inconsistent state. Preventing multiple tasks from interacting with the same instance simultaneously prevents problems like the following sequence of events:
update(with:)
メソッドは既にアクター上で動作しています、なのでそれはmax
のようなプロパティへのそれのアクセスをawait
で印しません。このメソッドはまた、なぜアクターそれらは一度にただ1つのタスクだけにそれらの可変状態との相互作用を許可するのかの理由の1つを示します:いくつかの更新をアクターのもつ状態に対してすることは一時的に不変式を壊します。TemperatureLogger
アクターは温度それらのリストと最大温度を追跡し続けます、そしてそれは最大温度をあなたが新しい測定を記録する時に更新します。更新の中途において、新しい測定を追加した後しかしmax
を更新する前に、TemperatureLoggerは一時的な矛盾状態に置かれます。複数のタスクが同じインスタンスと同時に相互作用するのを防ぐことは、以下の一連の出来事のような問題を防ぎます:
- Your code calls the
update(with:)
method. It updates themeasurements
array first. あなたのコードがupdate(with:)
メソッドを呼び出します。それはmeasurements
配列を最初に更新します。 - Before your code can update
max
, code elsewhere reads the maximum value and the array of temperatures. あなたのコードがmax
を更新可能になる前に、コードがどこか他のところで最大値と温度配列とを読み出します。 - Your code finishes its update by changing
max
. あなたのコードは、その更新をmax
を変更することによって終了します。
In this case, the code running elsewhere would read incorrect information because its access to the actor was interleaved in the middle of the call to update(with:)
while the data was temporarily invalid. You can prevent this problem when using Swift actors because they only allow one operation on their state at a time, and because that code can be interrupted only in places where await
marks a suspension point. Because update(with:)
doesn’t contain any suspension points, no other code can access the data in the middle of an update.
この場合には、どこか他で動作しているコードは正しくない情報を読み出します、なぜならアクターへのそれのアクセスは、update(with:)
への呼び出しの中途に、そのデータが一時的に正しくなかった間に、挟み込まれたからです。あなたはこの問題を防ぐことがスウィフトのアクターを使っている場合は可能です、なぜならそれらの状態に関して一度に1つの演算が許可されるだけだからです、そしてなぜならそのコードが割り込まれるのが可能なのはただawait
が中断地点を印するところにおいてのみだからです。update(with:)
がまったく中断地点を含まないことから、1つとして他のコードは更新の中途でデータにアクセスできません。
If you try to access those properties from outside the actor, like you would with an instance of a class, you’ll get a compile-time error; for example: あなたがそれらプロパティにアクターの外側からアクセスを、あなたがクラスのインスタンスでするように、試みるならば、あなたはコンパイル時エラーを得るでしょう;例えば:
- print(logger.max) // Error
Accessing logger.max
without writing await
fails because the properties of an actor are part of that actor’s isolated local state. Swift guarantees that only code inside an actor can access the actor’s local state. This guarantee is known as actor isolation.
logger.max
にawait
を書くことなくアクセスすることは失敗します、なぜならアクターのプロパティはそのアクターのもつ隔離されたローカルな状態の一部だからです。スウィフトは、あるアクター内部のコードだけが、そのアクターのもつローカル状態にアクセス可能であることを保証します。この保証は、アクター隔離(アクターアイソレーション)として知られます。