2022年11月28日月曜日

コルーチンを勉強する

 最近、機械学習系のライブラリとかゲームのライブラリとか動画を扱うライブラリがコルーチンという方法で書かかれているのをよく見ます。

Async/awaitパターンとも言われているそうです。


Async/await

https://ja.wikipedia.org/wiki/Async/await



パイプラインとかのような複雑な非同期非ブロッキングの関数を普通の同期関数と同じように記述できるから便利らしいです。たしかに機械学習の学習とか時間のかかる関数とかを簡単にマルチスレッド化できるし。

気が付けば、C言語も、C++言語も、Pythonも、JavaScriptも、ありとあらゆる言語が、コルーチンをサポートし、いろんなライブラリがそれで書かれている。

基本どのプログラム言語にも、async、awaitってものがあって、promiseというものに結果を格納するらしい。


しかしこの概念、久々に見た衝撃の難しさ。

いろいろな説明ページ見ても何言ってんの?っていうくらい難しい。

日本語でC++で正しく書かれているサイトないし。

年取るとこういう概念にとっさについていけない。

そもそもプロミスって最短30分くらいで簡単にお借入れできなかったっけ?

朝から夜まで一週間くらいずっと眺めて、やっとさっき主要な言語のpromiseの仕組みがわかかった。



C++のサンプル

-----------------------------------------

#include <coroutine>

#include <iostream>

#include <stdexcept>

#include <thread>


auto switch_to_new_thread(std::jthread& out) {

    struct awaitable {

        std::jthread* p_out;

        bool await_ready() { return false; }

        void await_suspend(std::coroutine_handle<> h) {

            std::jthread& out = *p_out;

            if (out.joinable())

                throw std::runtime_error("Output jthread parameter not empty");

            out = std::jthread([h] { h.resume(); });

            //潜在的な未定義の動作:潜在的に破壊された*thisへのアクセス

            // std :: cout << "新しいスレッドID:" << p_out-> get_id()<<'\ n';

            std::cout << "New thread ID: " << out.get_id() << '\n'; // これで結構です

        }

        void await_resume() {}

    };

    return awaitable{ &out };

}


struct task {

    struct promise_type {

        task get_return_object() { return {}; }

        std::suspend_never initial_suspend() { return {}; }

        std::suspend_never final_suspend() noexcept { return {}; }

        void return_void() {}

        void unhandled_exception() {}

    };

};


task resuming_on_new_thread(std::jthread& out) {

    std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n';

    co_await switch_to_new_thread(out);

    //ここでウェイターが破壊されました

    std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n';

}


int main() {

    std::jthread out;

    resuming_on_new_thread(out);

}

-----------------------------------------