【Unity】UniTask入門-Part1 非同期処理とはなんぞや!

Tips

こんにちは!saba🐟です!

最近Unityを使用しているとよくUniTaskやらUniRxの話を耳にするのですが、自分自身いまいちわかっていないので勉強していかなきゃな~と思っていました。
ですので、今回はUniTaskについて自分が調べたことを数回に分けてまとめていきます!
みなさんと一緒に学んでいけたらと思いますのでよろしくお願いします!

今回のpart1ではUniTaskを学ぶ上で必須の知識についてまとめていきます。

それでは皆さん頑張っていきましょう!

今回UniTaskについて調べていく中で細心の注意は払いますが、間違った知識を記載してしまう可能性があります。その点ご了承いただいたうえで見ていただけると大変助かります!
(もし間違いを見つけたら、コメントやお問い合わせなどで教えていただけるとありがたいです!)

UniTaskを学ぶ前に..

まずUniTaskを学ぶ前にそもそも非同期処理とはなんぞや!というところから話していこうと思います。

非同期処理はとても便利ですが、しっかり考えてから実装しないと処理が追いづらくなったり、処理時間が長くなったりと悪い面もありますのでしっかり勉強していきましょう!

同期処理と非同期処理の違い

同期処理とは

非同期処理について話す前にまずは同期処理について説明します。

同期処理とは、複数の処理を実行するときにひとつずつ順番に処理が実行されていくものをいいます。

例えばしたの画像のように処理A処理Bを呼び出した後、処理Bが終わってから処理Aが再開しています。

同期処理の図

この処理の流れは自分たちがよく知っている動きですよね!

非同期処理とは

逆に非同期処理とは、処理A処理Bを呼び出した後に、処理Bの終了を待たずに処理Aに戻ってくるもののことです!

下の非同期処理の図のように呼び出した後、に呼び出しが完了した時点で、元の処理が動き始めています。

非同期処理の図

最初私は「非同期処理はマルチスレッド処理なんだ!」と勘違いしてしまったのですが、そうではないみたいです。非同期処理は同期をしないだけのことでマルチスレッドと相性がよく、一緒に用いられることが多いのですが、本来は関係ないとのことです。シングルスレッドでも非同期処理はできるみたいですね!笑

コルーチン

次は自分が普段Unityで使っている、コルーチンについて紹介していきます。

コルーチンを使うことで非同期処理を使うことができるので普段自分はこれをメインに使ってきました。Unityでゲームを作る上で、任意の箇所で止めたり始めたり数秒待ったりなどの処理を使う機会が多いため愛用してきました。

コルーチンを使った便利な処理をいくつか紹介します。

1フレーム待ちたい場合
//1フレーム待つ
yield return null;
1秒待ちたい場合
//1秒待つ
yield return new WaitForSeconds(1f);
特定の処理があるまで待ちたい場合
// スペースキーが押されるまで待つ
yield return new WaitUntil(()=>Input.GetKeyDown(KeyCode.Space));

などとても便利な処理がたくさんあります。しかしこれらの処理を使用するには、IEnumeratorという戻り値の型で関数を宣言しなくてはなりません。従って処理をした値を戻り値として返すことができなかったりします。そこがコルーチンの欠点だったりします。

async/awaitについて

async/await は非同期処理を簡単にできるようにした C# が標準で持つ機能で、C#5.0以降で使用することができます。Unityだと、Unity2018以降からC#6がつかえるようになったのでUnity界隈では比較的新しめの機能ですね!

asyncは非同期という意味の asynchronous の略字で awaitはそのままの意味で待つという意味です。読み方はアシンク、アウェイトという人を結構見かけますが、

私は、エイシンク、ウェイト派です!

async
 asyncとはawaitをメソッド内で使用するためにつけるキーワードです。

await
 awaitを使うことでTaskの終了を待つことができます。
 (コルーチンで言うところのyield returnのようなものです。)

async/awaitの使い方
async 戻り値の型 関数名( 引数 )
{
    awaitが入る非同期処理や同期処理を入れる。
}

asyncの戻り値の型について

asyncの戻り値については、基本的には非同期の戻り値のために用意された型(Task、Task<T>、ValueTask<T>など)を実装した型を指定してあげる必要があります。

 Task
  操作を実行し、値を返さない非同期メソッドの場合に使用しましょう!

 Task<T>
  値を返したい場合ジェネリックのTの部分を自分が返したい型にしてあげます。
  (例 : string型を返したい場合 Task<string>)

もっと詳しく知りたい方はこちらの公式ドキュメントをご覧ください。

非同期の完了を待つ必要がない場合は、voidも使うことはできますが、async-void 警察に怒られるのでやめましょう笑

なぜUniTaskを使用するのか

最後になぜUniTaskを使用するかについて話していきたいと思います。

async/awaitでTaskの話をしたのですが、このTaskがUnityでasync/awaitを使いづらくしている原因でもあります。

このTaskを使用していくことで、Taskがさまざまな場所で生成されてしまい裏で動き続けてしまうこともあります。この状態が続くと最悪の場合、メモリリークしてしまうことも考えられるそうです。

さらに、この状態をUnity側から確認ができないので、メモリに残り続けていることが気づけないことも・・・。

また、UnityのAPIは基本的にメインスレッド以外で使用することもできないのですが、Taskはマルチスレッドの機能を含んでしまっています。なので、うっかりしているとマルチスレッドで動かしてしまったい、エラーを吐いてしまうこともあります。(UnityのAPIはシングルスレッドでしか動かないので、、)

これらの欠点を解決するために登場したのがUniTaskなのです。次回はこちらのUniTaskについて深掘りして学んでいきたいと思います。

まとめ

今回はUniTaskを学ぶ上で最低限つけておきたい知識についてまとめていきました。

今回自分自身も調べていて忘れていたり、なんとなく使っている部分が多々あったので良い復習になったと思います。

次回からは、UniTaskについて本格的に学んでいきたいと思います!

ではでは!

↓次の記事はこちら↓

コメント

  1. りゅうちゃん より:

    勉強になったよ(*^^)v

タイトルとURLをコピーしました