はかせのラボ

私の頭の中を書いていく雑記ブログです

C++ タスクシステム② ~コルーチン~

あいさつ

どうも、はかせです。
タスクシステム開発2日目です。
今日はコルーチンの勉強です。

コルーチンとは

処理の中断と再開が出来るメソッドです。
これを使えばタイマーみたいな一時停止が出来たりするものや、
一度にまとめてやると重い処理を分割して軽く見せたりできます。
そして中断と再開が出来るので現在の処理の状態も
うまくやればとれるはずです。

あと非同期系の処理をするときも基本になっているようなので
しっかり勉強しなきゃですね。

実装方法

コルーチンの歴史は結構長くて色々やり方があります。
ただ考え方自体は大きく変わっていないらしく昔の記事も参考にできるそうです。

今回は初回なので調べた中で一番楽にかつ使いやすそうなものを実装してみます。

std::experimental::generatorを使う

※experimentalと付いてるので今後変更されるかもしれません
std::experimental::generatorというのは
末尾の見えない集合を扱いやすくするものだそうです。
まぁ言ってしまえばコルーチンをやりやすくしてくれるものです。

C++のコルーチンにはいくつか満たさなければならない条件があるようなのですが
std::experimental::generatorはそれを満たした型っぽいです。
(experimentalと付いてるからかあまりしっかりした解説や
リファレンスを発見できませんでした。
しっかり知識をついていればヘッダを見てわかるのかもですが、
私はわかりませんでした。)

なのでこれを使うと意外とさっくり実装できます。
コードです。

int main(int argc, char const* argv[])
{
	auto text = "";
	auto test = new TestAsyncs();
	//100を10回で出力
	auto coroutine = test->TestNoticeCoroutine(100, 10);
	for (auto t : coroutine)
	{
		if (t) text = "true";
		else text = "false";
		cout << "Not Coroutine:" << text << endl;
		//処理が完了していたら終了
		if (t) break;
		//1秒待つ
		Sleep(1000);
	}
	getchar();
}

/*コルーチン*/
std::experimental::generator<bool> TestAsyncs::TestNoticeCoroutine(int num, int takeNum)
{
	for (int i = 1; i <= num; i++)
	{
		std::cout << "Coroutine:" << i << std::endl;
		//全て処理した
		if(i >= num)
		{
			co_yield true;
		}
		//takeNumの倍数ならば処理を中断
		if (i % takeNum == 0)
		{
			co_yield false;
		}
	}
	co_yield true;
}

実行結果です。
f:id:hakase0274:20181219235211g:plain

10回ずつ切り分けられてますね。

あとがき

今回はコルーチン初歩でした。
今回作ったものは楽で使いやすいのですが
細かいニーズには対応できないそうです。

なのでできない場合は自分で作る必要があるらしいです。
次回はそのあたりをやっていきたいなと思っています。
それでは今回はこの辺でノシ

参考

qiita.com