簡単にわかるTaskとactorの関係(Swift)

# 簡単にわかる`Task`と`actor`の関係

## `Task`とは

proposal [Structured concurrency](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0304-structured-concurrency.md)によると、
> A task is the basic unit of concurrency in the system.
>（直訳）一つのタスクはそのシステムでの平行性の基本的な単位です。

平行処理の単位です。一つのtaskでは同時に複数の関数が走ることはありません。必ず一つの関数が走ります。実際上記ドキュメントには以下のようなことが書かれています。

> A task runs one function at a time; a single task has no concurrency.
>（直訳）一つのタスクは同時に一つの関数を実行します。つまり、一つのタスクには並行性を持ちません。

swiftドキュメント [Tasks and Task Groups](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/#Tasks-and-Task-Groups)でも、

> A task itself does only one thing at a time,
>(直訳）一つのタスクは同時に一つのことを実行します。

と言っており、一つの`Task`は一つのことしかできません。

これがなかなか直感的にイメージしづらいかもしれませんが、上記proposalによるわかりやすい例えばありました。

>Every asynchronous function is executing in a task. In other words, a task is to asynchronous functions, what a thread is to synchronous functions.
>(直訳) すべての非同期関数は一つのTask内で実行します。言い換えると、一つのタスクと非同期関数の対応関係は、一つのthreadと同期関数の関係に等しい。

つまり、threadと同期関数の関係をイメージすれば、一つのtaskで複数の動作が走ることはないとわかると思います。

:::note info
あるタスク内で別のタスクを作った場合は、**複数の処理が平行して走ります**。(`Task`が**並行処理の単位**だからです。）  

これはスレッドの世界で「ある関数内で別スレッドを立ち上げる」のと同じようなイメージです。
:::

threadと同期関数がイメージつかない人は以下を読んでみてください。

```swift
func doSomething() -> String {
 // 何かする
}

// 何らかのthreadで動いている。
func mainFunction() {
  let result = doSomething()
  // result以降の処理
}
```

`mainFuction`は何らかのthreadで動いています。そして、`doSomething`関数も`mainFunction`と同じthreadで動いています。`someFunction`が呼び出されているからといって、そのthreadが止まっているわけではなく、そのthreadで動いています。

これと同じで、一つの`Task`しか使わない場合、平行処理の仕組みを使っているのに、同時に走っているのは一つの処理ということになります。なので、同時に処理を走らせたければ、複数`Task`を立ち上げる必要があるわけです。そこで問題になるのが、複数の`Task`でmutableな状態を共有するケースです。従来のSwiftでは状態を共有できる型として、`class`型を提供しています。しかし残念ながら、`class`型を使う場合はデータ競合を防ぐために自前で制御する必要があります。そこで出てくるのが、`actor`です。


## `actor`とは

`actor`とは、複数`Task`間でmutableな状態を安全に共有できるようにした型です。

swift document [Actors](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/#Actors)によると、

> 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.
(直訳) Task達はお互いに分離されており、それらが同時に実行しても安全です、しかし時々、あなたはTask間で情報を共有したいケースがあります。Actorは平行コード間で安全に情報を共有できるようにします。

最初に平行処理の単位は`Task`と述べました。つまり、複数同時に走らせるには複数の`Task`を立ち上げる必要があります。情報を共有したい場合は、`actor`を使えと言っています。(正確には、Task間でmutableな状態を共有したいケースです。)

proposal [`Actors`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0306-actors.md)を読むと、

> Swift includes classes, which provide a mechanism for declaring mutable state that is shared across the program. Classes, however, are notoriously difficult to correctly use within concurrent programs, requiring error-prone manual synchronization to avoid data races. We want to provide the ability to use shared mutable state while still providing static detection of data races and other common concurrency bugs.
(意訳)Swiftはclassをもち、それはプログラム間にまたがってmutableな状態を共有する仕組みを提供する。しかしながら、classは平行プログラム内で正しく使うのは悪名高く困難であり、データ競合を避けるためにはエラーを招きやすい手動の同期処理が必要になります。私たちはmutableな状態を共有するできるようにする一方で、データ競合や平行バグの静的な検知をできるようにしたい。

`class`は皆さんもご存知の通りデータ競合を防ぐための処理を自前で書く必要があります。Swift Concurrencyの世界で、`actor`はその手動で管理する作業からプログラムを開放してくれます。

## まとめ

今回は`Task`と`actor`の関係をまとめてみました。まず、`Task`という平行処理の単位があり、一つの`Task`のみだと同時に実行できるのは一つです。ですから、同時に複数処理を走らせたければ、複数の`Task`を走らせる必要があります。その際、複数の`Task`でmutableな状態を安全に共有できるようにしたのが`actor`なわけです。