同時実行性
日本語を消す 英語を消す下記URLから引用し、日本語訳をつけてみました。
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency
Perform asynchronous operations.
非同期操作を実行します。
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, and it suspends operations that are waiting for an external system.
Swift には、構造化された方法で非同期および並列コードを作成するためのサポートが組み込まれています。 非同期コードは一時停止して後で再開することができますが、一度に実行されるプログラムは 1 つだけです。 プログラム内のコードを一時停止および再開すると、ネットワーク経由でのデータの取得やファイルの解析などの長時間実行される操作を継続しながら、UI の更新などの短期的な操作を続行できます。 並列コードとは、複数のコードが同時に実行されることを意味します。たとえば、4 コア プロセッサを搭載したコンピューターは、各コアがいずれかのタスクを実行して、4 つのコードを同時に実行できます。 並列非同期コードを使用するプログラムは、一度に複数の操作を実行し、外部システムを待機している操作を一時停止します。
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.
並列または非同期コードによるスケジューリングの柔軟性の向上には、複雑さの増加というコストも伴います。 Swift を使用すると、コンパイル時のチェックを可能にする方法で意図を表現できます。たとえば、アクターを使用して可変状態に安全にアクセスできます。 ただし、遅いコードやバグのあるコードに同時実行性を追加しても、それが速くなったり正しくなったりするという保証はありません。 実際、同時実行性を追加すると、コードのデバッグがさらに難しくなる可能性があります。 ただし、同時実行が必要なコードで Swift の言語レベルの同時実行サポートを使用すると、Swift はコンパイル時に問題を検出できるようになります。
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. When an asynchronous function resumes, Swift doesn’t make any guarantee about which thread that function will run on.
以前に同時実行コードを書いたことがある場合は、スレッドの操作に慣れているかもしれません。 Swift の同時実行モデルはスレッド上に構築されますが、スレッドと直接対話することはありません。 Swift の非同期関数は、実行中のスレッドを放棄することができます。これにより、最初の関数がブロックされている間、そのスレッド上で別の非同期関数が実行されるようになります。 非同期関数が再開されるとき、Swift はその関数がどのスレッドで実行されるかについて保証しません。
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:
Swift の言語サポートを使用せずに同時コードを記述することは可能ですが、そのコードは読みにくくなる傾向があります。 たとえば、次のコードは写真名のリストをダウンロードし、そのリストの最初の写真をダウンロードして、その写真をユーザーに表示します。
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:
関数またはメソッドが非同期であることを示すには、throws
を使用してスロー関数をマークする方法と同様に、宣言内のパラメーターの後に async
キーワードを記述します。 関数またはメソッドが値を返す場合は、戻り矢印 (->
) の前に「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
.
非同期とスローの両方を行う関数またはメソッドの場合は、throws
の前に async
を記述します。
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
. Marking all of the possible suspension points in your code helps make concurrent code easier to read and understand.
非同期メソッドを呼び出すと、そのメソッドが返されるまで実行が一時停止されます。 呼び出し前に「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 つを次に示します。
- 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. - 1.コードは最初の行から実行を開始し、最初の
await
まで実行されます。listPhotos(inGallery:)
関数を呼び出し、その関数が返されるのを待つ間実行を一時停止します。 - 2.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. - 2.このコードの実行が一時停止されている間、同じプログラム内の他の同時コードが実行されます。 たとえば、長時間実行されるバックグラウンド タスクが新しいフォト ギャラリーのリストを更新し続ける可能性があります。 このコードは、
await
でマークされた次の一時停止ポイントまで、または完了するまで実行されます。 - 3.After
listPhotos(inGallery:)
returns, this code continues execution starting at that point. It assigns the value that was returned tophotoNames
. - 3.
listPhotos(inGallery:)
が返された後、このコードはその時点から実行を続けます。 返された値をphotoNames
に割り当てます。 - 4.The lines that define
sortedNames
andname
are regular, synchronous code. Because nothing is markedawait
on these lines, there aren’t any possible suspension points. - 4.
sortedNames
とname
を定義する行は、通常の同期コードです。 これらの行には何もawait
マークが付けられていないため、一時停止ポイントとなる可能性はありません。 - 5.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. - 5.次の
await
は、downloadPhoto(named:)
関数の呼び出しをマークします。 このコードは、その関数が戻るまで実行を再び一時停止し、他の同時実行コードに実行の機会を与えます。 - 6.After
downloadPhoto(named:)
returns, its return value is assigned tophoto
and then passed as an argument when callingshow(_:)
. - 6.
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
」マークが付いている一時停止ポイントの可能性は、非同期関数またはメソッドが返されるのを待っている間に、現在のコード部分が実行を一時停止する可能性があることを示しています。 これは、Swift がバックグラウンドで現在のスレッドでのコードの実行を一時停止し、代わりにそのスレッドで他のコードを実行するため、スレッドの譲歩とも呼ばれます。 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.
- 以下の「非構造化同時実行」に示すように、非構造化子タスクをコード化します。
You can explicitly insert a suspension point by calling the Task.yield()
(Link:developer.apple.com) method.
(Link:developer.apple.com)(英語) メソッドを呼び出すことで、一時停止ポイントを明示的に挿入できます。Task.yield()
func generateSlideshow(forGallery gallery: String) async {
let photos = await listPhotos(inGallery: gallery)
for photo in photos {
// ... render a few seconds of video for this photo ...
await Task.yield()
}
}
Assuming the code that renders video is synchronous, it doesn’t contain any suspension points. The work to render video could also take a long time. However, you can periodically call Task.yield()
to explicitly add suspension points. Structuring long-running code this way lets Swift balance between making progress on this task, and letting other tasks in your program make progress on their work.
ビデオをレンダリングするコードが同期していると仮定すると、一時停止ポイントは含まれません。 ビデオをレンダリングする作業にも時間がかかる場合があります。 ただし、Task.yield() を定期的に呼び出して、一時停止ポイントを明示的に追加することができます。 この方法で長時間実行されるコードを構造化すると、Swift はこのタスクの進捗と、プログラム内の他のタスクの作業の進捗との間でバランスをとることができます。
The Task.sleep(for:tolerance:clock:)
(Link:developer.apple.com) method is useful when writing simple code to learn how concurrency works. This method suspends the current task for at least the given amount of time. Here’s a version of the listPhotos(inGallery:)
function that uses sleep(for:tolerance:clock:)
to simulate waiting for a network operation:
Task.sleep(for:tolerance: Clock:)
(Link:developer.apple.com)(英語) メソッドは、同時実行の仕組みを学ぶために簡単なコードを作成する場合に役立ちます。 このメソッドは、少なくとも指定された時間、現在のタスクを一時停止します。 以下は、sleep(for:tolerance: Clock:)
を使用してネットワーク操作の待機をシミュレートする listPhotos(inGallery:)
関数のバージョンです。
func listPhotos(inGallery name: String) async throws -> [String] {
try await Task.sleep(for: .seconds(2))
return ["IMG001", "IMG99", "IMG0404"]
}
The version of listPhotos(inGallery:)
in the code above is both asynchronous and throwing, because the call to Task.sleep(until:tolerance:clock:)
can throw an error. When you call this version of listPhotos(inGallery:)
, you write both try
and await
:
上記のコードの listPhotos(inGallery:)
のバージョンは、Task.sleep(until:tolerance: Clock:)
の呼び出しでエラーがスローされる可能性があるため、非同期かつスローの両方です。 このバージョンの listPhotos(inGallery:)
を呼び出すときは、try
と await
の両方を記述します。
let photos = try await listPhotos(inGallery: "A Rainy Weekend")
Asynchronous functions have some similarities to throwing functions: When you define an asynchronous or throwing function, you mark it with async
or throws
, and you mark calls to that function with await
or try
. An asynchronous function can call another asynchronous function, just like a throwing function can call another throwing function.
非同期関数にはスロー関数との類似点がいくつかあります。非同期関数またはスロー関数を定義するときは、それを async
または throws
でマークし、その関数の呼び出しを await
または try
でマークします。 非同期関数は、スロー関数が別のスロー関数を呼び出すことができるのと同じように、別の非同期関数を呼び出すことができます。
However, there’s a very important difference. You can wrap throwing code in a do
–catch
block to handle errors, or use Result
to store the error for code elsewhere to handle it. These approaches let you call throwing functions from nonthrowing code. For example:
ただし、非常に重要な違いがあります。 スローコードを do-catch
ブロックでラップしてエラーを処理したり、Result
を使用してコードのエラーを別の場所に保存して処理したりできます。 これらのアプローチにより、非スロー コードからスロー関数を呼び出すことができます。 例えば:
func getRainyWeekendPhotos() async -> Result<[String]> {
return Result {
try await listPhotos(inGallery: "A Rainy Weekend")
}
}
In contrast, there’s no safe way to wrap asynchronous code so you can call it from synchronous code and wait for the result. The Swift standard library intentionally omits this unsafe functionality — trying to implement it yourself can lead to problems like subtle races, threading issues, and deadlocks. When adding concurrent code to an existing project, work from the top down. Specifically, start by converting the top-most layer of code to use concurrency, and then start converting the functions and methods that it calls, working through the project’s architecture one layer at a time. There’s no way to take a bottom-up approach, because synchronous code can’t ever call asynchronous code.
対照的に、同期コードから呼び出して結果を待つことができるように非同期コードをラップする安全な方法はありません。 Swift 標準ライブラリは、この安全でない機能を意図的に省略しています。これを自分で実装しようとすると、微妙な競合、スレッドの問題、デッドロックなどの問題が発生する可能性があります。 既存のプロジェクトに並行コードを追加する場合は、上から下に作業します。 具体的には、同時実行性を使用するようにコードの最上位層を変換することから始め、次に、プロジェクトのアーキテクチャを一度に 1 層ずつ処理しながら、その層が呼び出す関数とメソッドの変換を開始します。 同期コードは非同期コードを呼び出すことができないため、ボトムアップ アプローチを取る方法はありません。
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 つのアプローチは、非同期シーケンスを使用して、一度にコレクションの 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
ループを使用する代わりに、その後に await
を付けて for
を記述しています。 非同期関数またはメソッドを呼び出すときと同様、「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
(Link:developer.apple.com) protocol, you can use your own types in a for
–await
–in
loop by adding conformance to the AsyncSequence
(Link:developer.apple.com) protocol.
Sequence
(Link:developer.apple.com)(英語) プロトコルに準拠を追加することで for-in
ループで独自の型を使用できるのと同じように、AsyncSequence
(Link:developer.apple.com)(英語) プロトコルに準拠を追加することで for-await-in
ループで独自の型を使用できます。
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.
このアプローチには重要な欠点があります。ダウンロードは非同期であり、ダウンロードの進行中に他の作業が行われますが、downloadPhoto(named:)
の呼び出しは一度に 1 つだけ実行されます。 各写真は完全にダウンロードされてから、次の写真のダウンロードが開始されます。 ただし、これらの操作を待つ必要はありません。各写真を個別にダウンロードすることも、同時にダウンロードすることもできます。
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.
非同期関数を呼び出してその周囲のコードと並行して実行するには、定数を定義するときに let
の前に async
を書き、定数を使用するたびに 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
が定義されている行まで実行が続行されます。その時点で、プログラムはこれらの非同期呼び出しの結果を必要とするため、3 枚の写真すべてのダウンロードが完了するまで実行を一時停止する await
を記述します。
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. A task itself does only one thing at a time, but when you create multiple tasks, Swift can schedule them to run simultaneously.
タスクは、プログラムの一部として非同期的に実行できる作業単位です。 すべての非同期コードは、何らかのタスクの一部として実行されます。 タスク自体は一度に 1 つのことしか実行しませんが、複数のタスクを作成すると、Swift はそれらを同時に実行するようにスケジュールできます。
The async
–let
syntax described in the previous section implicitly creates a child task — this syntax works well when you already know what tasks your program needs to run. You can also create a task group (an instance of TaskGroup
(Link:developer.apple.com)) and explicitly 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
構文は、子タスクを暗黙的に作成します。この構文は、プログラムで実行する必要があるタスクがすでにわかっている場合にうまく機能します。 タスク グループ(TaskGroup
(Link:developer.apple.com)(英語)のインスタンス)を作成し、そのグループに子タスクを明示的に追加することもできます。これにより、優先度とキャンセルをより細かく制御できるようになり、動的な数のタスクを作成できるようになります。
Tasks are arranged in a hierarchy. Each task in a given 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. The explicit parent-child relationships between tasks has several advantages:
タスクは階層的に配置されます。 特定のタスク グループ内の各タスクには同じ親タスクがあり、各タスクは子タスクを持つことができます。 タスクとタスクグループの間には明示的な関係があるため、このアプローチは構造化された同時実行性と呼ばれます。 タスク間の明示的な親子関係には、いくつかの利点があります。
- In a parent task, you can’t forget to wait for its child tasks to complete.
- 親タスクでは、子タスクが完了するまで待つことを忘れてはなりません。
- When setting a higher priority on a child task, the parent task’s priority is automatically escalated.
- 子タスクに高い優先度を設定すると、親タスクの優先度が自動的に上がります。
- When a parent task is canceled, each of its child tasks is also automatically canceled.
- 親タスクがキャンセルされると、その子タスクもそれぞれ自動的にキャンセルされます。
- Task-local values propagate to child tasks efficiently and automatically.
- タスクローカル値は、子タスクに効率的かつ自動的に伝播されます。
Here’s another version of the code to download photos that handles any number of photos:
以下は、任意の数の写真を処理する写真をダウンロードするコードの別のバージョンです。
await withTaskGroup(of: Data.self) { group in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
group.addTask {
return await downloadPhoto(named: name)
}
}
for await photo in group {
show(photo)
}
}
The code above creates a new task group, and then creates child tasks to download each photo in the gallery. Swift runs as many of these tasks concurrently as conditions allow. As soon a child task finishes downloading a photo, that photo is displayed. There’s no guarantee about the order that child tasks complete, so the photos from this gallery can be shown in any order.
上記のコードは、新しいタスク グループを作成し、ギャラリー内の各写真をダウンロードするための子タスクを作成します。 Swift は、条件が許す限りこれらのタスクをできるだけ多く同時に実行します。 子タスクが写真のダウンロードを完了するとすぐに、その写真が表示されます。 子タスクが完了する順序については保証がないため、このギャラリーの写真は任意の順序で表示されます。
Note
注釈
If the code to download a photo could throw an error, you would call withThrowingTaskGroup(of:returning:body:)
instead.
写真をダウンロードするコードでエラーがスローされる可能性がある場合は、代わりに withThrowingTaskGroup(of:returning:body:)
を呼び出します。
In the code listing above, each photo is downloaded and then displayed, so the task group doesn’t return any results. For a task group that returns a result, you add code that accumulates its result inside the closure you pass to withTaskGroup(of:returning:body:)
.
上記のコード リストでは、各写真がダウンロードされて表示されるため、タスク グループは結果を返しません。 結果を返すタスク グループの場合は、withTaskGroup(of:returning:body:)
に渡すクロージャ内に結果を蓄積するコードを追加します。
let photos = await withTaskGroup(of: Data.self) { group in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
group.addTask {
return await downloadPhoto(named: name)
}
}
var results: [Data] = []
for await photo in group {
results.append(photo)
}
return results
}
Like the previous example, this example creates a child task for each photo to download it. Unlike the previous example, the for
–await
–in
loop waits for the next child task to finish, appends the result of that task to the array of results, and then continues waiting until all child tasks have finished. Finally, the task group returns the array of downloaded photos as its overall result.
前の例と同様に、この例では写真ごとに子タスクを作成してダウンロードします。 前の例とは異なり、for-await-in
ループは次の子タスクが終了するのを待ち、そのタスクの結果を結果の配列に追加して、すべての子タスクが終了するまで待機し続けます。 最後に、タスク グループは、ダウンロードされた写真の配列を全体的な結果として返します。
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 appropriately. Depending on what work the task is doing, responding to cancellation usually means one of the following:
Swift の同時実行では、協調キャンセル モデルが使用されます。 各タスクは、実行中の適切な時点でキャンセルされたかどうかを確認し、キャンセルに適切に応答します。 タスクが実行している作業に応じて、キャンセルへの応答は通常、次のいずれかを意味します。
- Throwing an error like
CancellationError
- CancelError のようなエラーをスローする
- Returning
nil
or an empty collection - nil または空のコレクションを返す
- Returning the partially completed work
- 途中まで完成した作品の返却
Downloading pictures could take a long time if the pictures are large or the network is slow. To let the user stop this work, without waiting for all of the tasks to complete, the tasks need check for cancellation and stop running if they are canceled. There are two ways a task can do this: by calling the Task.checkCancellation()
(Link:developer.apple.com) method, or by reading the Task.isCancelled
(Link:developer.apple.com) property. Calling checkCancellation()
throws an error if the task is canceled; a throwing task can propagate the error out of the task, stopping all of the task’s work. This has the advantage of being simple to implement and understand. For more flexibility, use the isCancelled
property, which lets you perform clean-up work as part of stopping the task, like closing network connections and deleting temporary files.
写真が大きい場合やネットワークが遅い場合は、写真のダウンロードに時間がかかることがあります。 すべてのタスクが完了するのを待たずにユーザーがこの作業を停止できるようにするには、タスクがキャンセルされているかどうかを確認し、キャンセルされた場合は実行を停止する必要があります。 タスクでこれを行うには 2 つの方法があります。1 つは Task.checkCancellation()
(Link:developer.apple.com)(英語) メソッドを呼び出すこと、もう 1 つは Task.isCancelled
(Link:developer.apple.com)(英語) プロパティを読み取ることです。 タスクがキャンセルされた場合、checkCancellation()
を呼び出すとエラーがスローされます。 タスクをスローすると、タスクの外にエラーが伝播し、タスクのすべての作業が停止する可能性があります。 これには、実装と理解が簡単であるという利点があります。 柔軟性を高めるには、isCancelled
プロパティを使用します。これにより、ネットワーク接続の終了や一時ファイルの削除など、タスクの停止の一環としてクリーンアップ作業を実行できます。
let photos = await withTaskGroup(of: Optional<Data>.self) { group in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
group.addTaskUnlessCancelled {
guard isCancelled == false else { return nil }
return await downloadPhoto(named: name)
}
}
var results: [Data] = []
for await photo in group {
if let photo { results.append(photo) }
}
return results
}
The code above makes several changes from the previous version:
上記のコードには、以前のバージョンからいくつかの変更が加えられています。
- Each task is added using the
TaskGroup.addTaskUnlessCancelled(priority:operation:)
(Link:developer.apple.com) method, to avoid starting new work after cancellation. - 各タスクは、キャンセル後に新しい作業が開始されないように、
TaskGroup.addTaskUnlessCancelled(priority:operation:)
(Link:developer.apple.com)(英語) メソッドを使用して追加されます。 - Each task checks for cancellation before starting to download the photo. If it has been canceled, the task returns
nil
. - 各タスクは、写真のダウンロードを開始する前にキャンセルを確認します。 キャンセルされた場合、タスクは
nil
を返します。 - At the end, the task group skips
nil
values when collecting the results. Handling cancellation by returningnil
means the task group can return a partial result — the photos that were already downloaded at the time of cancellation — instead of discarding that completed work. - 最後に、タスク グループは結果を収集するときに
nil
値をスキップします。nil
を返してキャンセルを処理するということは、タスク グループが完了した作業を破棄する代わりに、部分的な結果 (キャンセル時に既にダウンロードされていた写真) を返すことができることを意味します。
For work that needs immediate notification of cancellation, use the Task.withTaskCancellationHandler(operation:onCancel:)
(Link:developer.apple.com) method. For example:
キャンセルの即時通知が必要な作業の場合は、Task.withTaskCancellationHandler(operation:onCancel:)
(Link:developer.apple.com)(英語) メソッドを使用します。 例えば:
let task = await Task.withTaskCancellationHandler {
// ...
} onCancel: {
print("Canceled!")
}
// ... some time later...
task.cancel() // Prints "Canceled!"
When using a cancellation handler, task cancellation is still cooperative: The task either runs to completion or checks for cancellation and stops early. Because the task is still running when the cancellation handler starts, avoid sharing state between the task and its cancellation handler, which could create a race condition.
キャンセル ハンドラーを使用する場合でも、タスクのキャンセルは協調的です。タスクは完了するまで実行されるか、キャンセルを確認して早期に停止します。 キャンセル ハンドラーの開始時にタスクはまだ実行中であるため、タスクとそのキャンセル ハンドラーの間で状態を共有しないようにしてください。競合状態が発生する可能性があります。
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:)
(Link:developer.apple.com) 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:)
(Link:developer.apple.com) class method. Both of these operations return a task that you can interact with — for example, to wait for its result or to cancel it.
前のセクションで説明した同時実行に対する構造化されたアプローチに加えて、Swift は非構造化同時実行もサポートしています。 タスク グループの一部であるタスクとは異なり、非構造化タスクには親タスクがありません。 プログラムに必要な方法で非構造化タスクを管理する完全な柔軟性を備えていますが、その正確さについては完全に責任を負います。 現在のアクター上で実行される非構造化タスクを作成するには、Task.init(priority:operation:)
(Link:developer.apple.com)(英語) イニシャライザーを呼び出します。 現在のアクターの一部ではない非構造化タスク、より具体的にはデタッチされたタスクとして知られるタスクを作成するには、Task.detached(priority:operation:)
(Link:developer.apple.com)(英語)クラス メソッドを呼び出します。 これらの操作は両方とも、結果を待つ、キャンセルするなどの操作が可能なタスクを返します。
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
(Link:developer.apple.com).
切り離されたタスクの管理の詳細については、「Task
(Link:developer.apple.com)(英語)」を参照してください。
Actors
アクター
You can use tasks to break up your program into isolated, concurrent pieces. Tasks are isolated from each other, which is what makes it safe for them to run at the same time, but sometimes you need to share some information between tasks. Actors let you safely share information between concurrent code.
タスクを使用すると、プログラムを独立した同時実行部分に分割できます。 タスクは相互に分離されているため、同時に実行しても安全ですが、場合によってはタスク間で情報を共有する必要があります。 アクターを使用すると、同時実行コード間で情報を安全に共有できます。
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
アクターには、アクターの外部の他のコードがアクセスできるプロパティがあり、アクター内のコードのみが最大値を更新できるように最大プロパティを制限します。
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 つのタスクのみがその可変状態へのアクセスを許可するため、別のタスクのコードがすでにロガーと対話している場合、このコードはプロパティへのアクセスを待機している間一時停止します。
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
を更新する前に、温度ロガーは一時的に矛盾した状態になります。 複数のタスクが同じインスタンスと対話しないようにすると、次の一連のイベントのような問題が同時に防止されます。
- 1.Your code calls the
update(with:)
method. It updates themeasurements
array first. - 1.コードは
update(with:)
メソッドを呼び出します。 最初にmeasurements
の配列を更新します。 - 2.Before your code can update
max
, code elsewhere reads the maximum value and the array of temperatures. - 2.コードが
max
を更新する前に、コードが他の場所で最大値と温度の配列を読み取ります。 - 3.Your code finishes its update by changing
max
. - 3.
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:)
の呼び出しの途中でインターリーブされたため、他の場所で実行されているコードは誤った情報を読み取ります。 Swift アクターを使用すると、状態に対して一度に 1 つの操作しか許可されず、await
が一時停止ポイントをマークする場所でのみコードを中断できるため、この問題を防ぐことができます。 update(with:)
には一時停止ポイントが含まれていないため、他のコードは更新中にデータにアクセスできません。
If code outside the actor tries to access those properties directly, like accessing a structure or class’s properties, 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. The code to access this property needs to run as part of the actor, which is an asynchronous operation and requires writing await
. Swift guarantees that only code running on an actor can access that actor’s local state. This guarantee is known as actor isolation.
アクターのプロパティはそのアクターの分離されたローカル状態の一部であるため、await
を書かずに logger.max
にアクセスすると失敗します。 このプロパティにアクセスするコードはアクターの一部として実行する必要があります。これは非同期操作であり、await
を記述する必要があります。 Swift は、アクター上で実行されているコードのみがそのアクターのローカル状態にアクセスできることを保証します。 この保証はアクターの分離として知られています。
The following aspects of the Swift concurrency model work together to make it easier to reason about shared mutable state:
Swift 同時実行モデルの次の側面が連携して、共有の可変状態についての推論を容易にします。
- Code in between possible suspension points runs sequentially, without the possibility of interruption from other concurrent code.
- 可能な一時停止ポイント間のコードは、他の同時コードから中断されることなく、順次実行されます。
- Code that interacts with an actor’s local state runs only on that actor.
- アクターのローカル状態と対話するコードは、そのアクター上でのみ実行されます。
- An actor runs only one piece of code at a time.
- アクターは一度に 1 つのコードのみを実行します。
Because of these guarantees, code that doesn’t include await
and that’s inside an actor can make the updates without a risk of other places in your program observing the temporarily invalid state. For example, the code below converts measured temperatures from Fahrenheit to Celsius:
これらの保証により、await
を含まず、アクター内にあるコードは、プログラム内の他の場所で一時的に無効な状態が観察されるリスクを負うことなく、更新を行うことができます。 たとえば、以下のコードは測定温度を華氏から摂氏に変換します。
extension TemperatureLogger {
func convertFarenheitToCelsius() {
measurements = measurements.map { measurement in
(measurement - 32) * 5 / 9
}
}
}
The code above converts the array of measurements, one at a time. While the map operation is in progress, some temperatures are in Fahrenheit and others are in Celsius. However, because none of the code includes await
, there are no potential suspension points in this method. The state that this method modifies belongs to the actor, which protects it against code reading or modifying it except when that code runs on the actor. This means there’s no way for other code to read a list of partially converted temperatures while unit conversion is in progress.
上記のコードは、測定値の配列を一度に 1 つずつ変換します。 マップ操作の進行中、一部の温度は華氏で表示され、その他は摂氏で表示されます。 ただし、コードに await
が含まれていないため、このメソッドには潜在的な一時停止ポイントがありません。 このメソッドが変更する状態はアクターに属し、そのコードがアクター上で実行される場合を除き、コードの読み取りや変更からアクターを保護します。 これは、単位変換の進行中に、他のコードが部分的に変換された温度のリストを読み取る方法がないことを意味します。
In addition to writing code in an actor that protects temporary invalid state by omitting potential suspension points, you can move that code into a synchronous method. The convertFarenheitToCelsius()
method above is method, so it’s guaranteed to never contain potential suspension points. This function encapsulates the code that temporarily makes the data model inconsistent, and makes it easier for anyone reading the code to recognize that no other code can run before data consistency is restored by completing the work. In the future, if you try to add concurrent code to this function, introducing a possible suspension point, you’ll get compile-time error instead of introducing a bug.
潜在的な一時停止ポイントを省略して一時的に無効な状態を保護するコードをアクターで作成するだけでなく、そのコードを同期メソッドに移動することもできます。 上記のconvertFarenheitToCelsius()
メソッドはメソッドであるため、潜在的な一時停止ポイントが含まれないことが保証されています。 この機能は、データ モデルの一貫性を一時的に失わせるコードをカプセル化し、作業を完了してデータの一貫性が回復するまでは他のコードを実行できないことを、コードを読む人が容易に認識できるようにします。 将来、この関数に並行コードを追加して一時停止ポイントが発生する可能性がある場合、バグが発生する代わりにコンパイル時エラーが発生します。
Sendable Types
送信可能なタイプ
Tasks and actors let you divide a program into pieces that can safely run concurrently. Inside of a task or an instance of an actor, the part of a program that contains mutable state, like variables and properties, is called a concurrency domain. Some kinds of data can’t be shared between concurrency domains, because that data contains mutable state, but it doesn’t protect against overlapping access.
タスクとアクターを使用すると、プログラムを安全に同時に実行できる部分に分割できます。 タスクまたはアクターのインスタンス内で、変数やプロパティなどの変更可能な状態を含むプログラムの部分は、同時実行ドメインと呼ばれます。 一部の種類のデータは同時実行ドメイン間で共有できません。そのデータには変更可能な状態が含まれているためですが、重複アクセスを防ぐことはできません。
A type that can be shared from one concurrency domain to another is known as a sendable type. For example, it can be passed as an argument when calling an actor method or be returned as the result of a task. The examples earlier in this chapter didn’t discuss sendability because those examples use simple value types that are always safe to share for the data being passed between concurrency domains. In contrast, some types aren’t safe to pass across concurrency domains. For example, a class that contains mutable properties and doesn’t serialize access to those properties can produce unpredictable and incorrect results when you pass instances of that class between different tasks.
ある同時実行ドメインから別の同時実行ドメインに共有できるタイプは、送信可能なタイプと呼ばれます。 たとえば、アクター メソッドを呼び出すときに引数として渡すことも、タスクの結果として返すこともできます。 この章の前半の例では、同時実行ドメイン間で受け渡されるデータに対して常に安全に共有できる単純な値型を使用しているため、送信可能性については説明しませんでした。 対照的に、一部の型は同時実行ドメイン間で渡すのが安全ではありません。 たとえば、変更可能なプロパティを含み、それらのプロパティへのアクセスをシリアル化していないクラスでは、異なるタスク間でそのクラスのインスタンスを渡すと、予測不能で不正確な結果が生成される可能性があります。
You mark a type as being sendable by declaring conformance to the Sendable
protocol. That protocol doesn’t have any code requirements, but it does have semantic requirements that Swift enforces. In general, there are three ways for a type to be sendable:
Sendable
プロトコルへの準拠を宣言することで、タイプを送信可能としてマークします。 このプロトコルにはコード要件はありませんが、Swift が強制するセマンティック要件はあります。 一般に、型を送信可能にする方法は 3 つあります。
- The type is a value type, and its mutable state is made up of other sendable data — for example, a structure with stored properties that are sendable or an enumeration with associated values that are sendable.
- この型は値型であり、その可変状態は他の送信可能なデータで構成されます。たとえば、送信可能な格納されたプロパティを持つ構造体や、送信可能な関連する値を持つ列挙型などです。
- The type doesn’t have any mutable state, and its immutable state is made up of other sendable data — for example, a structure or class that has only read-only properties.
- この型には可変状態はなく、その不変状態は他の送信可能なデータ (読み取り専用プロパティのみを持つ構造体やクラスなど) で構成されます。
- The type has code that ensures the safety of its mutable state, like a class that’s marked
@MainActor
or a class that serializes access to its properties on a particular thread or queue. - この型には、
@MainActor
とマークされたクラスや、特定のスレッドまたはキューでそのプロパティへのアクセスをシリアル化するクラスなど、可変状態の安全性を確保するコードが含まれています。
For a detailed list of the semantic requirements, see the Sendable
(Link:developer.apple.com) protocol reference.
セマンティック要件の詳細なリストについては、Sendable
(Link:developer.apple.com)(英語) プロトコルのリファレンスをご覧ください。
Some types are always sendable, like structures that have only sendable properties and enumerations that have only sendable associated values. For example:
送信可能なプロパティのみを持つ構造体や、送信可能な関連値のみを持つ列挙型など、一部の型は常に送信可能です。 例えば:
struct TemperatureReading: Sendable {
var measurement: Int
}
extension TemperatureLogger {
func addReading(from reading: TemperatureReading) {
measurements.append(reading.measurement)
}
}
let logger = TemperatureLogger(label: "Tea kettle", measurement: 85)
let reading = TemperatureReading(measurement: 45)
await logger.addReading(from: reading)
Because TemperatureReading
is a structure that has only sendable properties, and the structure isn’t marked public
or @usableFromInline
, it’s implicitly sendable. Here’s a version of the structure where conformance to the Sendable
protocol is implied:
TemperatureReading
は送信可能なプロパティのみを持つ構造であり、その構造は public
または @usableFromInline
としてマークされていないため、暗黙的に送信可能です。 Sendable
プロトコルへの準拠が暗黙的に示される構造のバージョンは次のとおりです。
struct TemperatureReading {
var measurement: Int
}
To explicitly mark a type as not being sendable, overriding an implicit conformance to the Sendable
protocol, use an extension:
型を送信不可として明示的にマークし、Sendable
プロトコルへの暗黙の準拠をオーバーライドするには、拡張機能を使用します。
struct FileDescriptor {
let rawValue: CInt
}
@available(*, unavailable) //←この属性は全てのプラットフォームで不能にします。
extension FileDescriptor: Sendable { }
The code above shows part of a wrapper around POSIX file descriptors. Even though interface for file descriptors uses integers to identify and interact with open files, and integer values are sendable, a file descriptor isn’t safe to send across concurrency domains.
上記のコードは、POSIX ファイル記述子のラッパーの一部を示しています。 ファイル記述子のインターフェイスでは、開いているファイルの識別と操作に整数が使用され、整数値は送信可能ですが、ファイル記述子を同時実行ドメイン間で送信するのは安全ではありません。
In the code above, the FileDescriptor
is a structure that meets the criteria to be implicitly sendable. However, the extension makes its conformance to Sendable
unavailable, preventing the type from being sendable.
上記のコードでは、FileDescriptor
は暗黙的に送信可能であるための基準を満たす構造体です。 ただし、この拡張機能により Sendable
への準拠が無効になり、その型を送信可能にすることができなくなります。