次のdmd 2.067から利用出来るstd.concurrencyのSchedulerについて書きます(これ書く頃には2.067が出るものと思ってました).
std.concurrencyに関しては,去年のAdvent Calendarに二つ記事が上がっているので,参考にしてみてください.
2.066までのstd.concurrency
2.066まで,std.concurrencyのAPIはspawnくらいで,また裏ではスレッドが起動するというシンプルなものでした.しかし.std.concurrencyを使ったライブラリも少しずつ増え,スケジューリング方法を制御したい,という要望も出てきました.
2.067からは,Schedulerというクラスを使うことで,std.concurrencyのAPIを使いつつ,自分の好きなスケジューリングが出来るようになります.下記はSchedulerが実装されたPull Requestです.
Issue 12090 - Make std.concurrency compatible with fibers as threads
Scheduler API
SchedulerのAPIは今のところ,Threadのようなimplicitなスケジューリングと,Fiberのようなexplicitなスケジューリングの二つをカバーする以下のAPIになってます.APIは要望やカバーするケースによって今後増えたりするかもしれません.
interface Scheduler
{
void start(void delegate() op);
void spawn(void delegate() op);
void yield() nothrow;
@property ref ThreadInfo thisInfo() nothrow;
Condition newCondition(Mutex m)
}
現状,このAPIの参照実装として,ThreadベースのThreadScheduler,FiberベースのFiberSchedulerの二つが提供されています.
APIについてそれぞれ説明します.
- start
opを実行してスケジューリングを開始します.これはFiberSchedulerのようなexplicitなスケジューリングが必要なスケジューラ向けです.Schedulerを使う時には,メインプログラムをopに中の入れ,startを呼び出すようにします.
- spawn
通常のspawnと同じセマンティクスです.ThreadSchedulerならThreadで,FiberSchedulerならFiberでopを起動します.
- yield
Fiberのyieldと同じセマンティクスです.explicitなFiberSchedulerではyieldを呼び,implicitなThreadSchedulerでは,空になっています.
- thisInfo
該当するThreadInfoオブジェクトを返します.ThreadInfoオブジェクトは,スケジューリングに必要なスレッド関係の情報を保持しているオブジェクトになります.これを経由して,スケジューラに関わっているデータをクリーンアップします.
- newCondition
Schedulerに特化した条件変数を返すために使います.通常であれば,条件変数のwaitが呼ばれたらブロッキングしたりタイムアウトしたりするのが普通ですが,その辺りの挙動を変更したい場合などは,特殊化された条件変数を返すようにします.
Schedulerの使い方
std.concurrencyにschedulerというグローバル変数があるので,これにSchedulerをセットします.そうするとstd.concurrencyの各操作は,このschedulerを参照して動くようになります.
以下の例は,FiberSchedulerを使うように変更する例です.
import std.stdio;
import std.concurrency;
void main()
{
scheduler = new FiberScheduler;
scheduler.start({
writeln("Hello!");
});
}
Schedulerの使い所
今のところ,ほとんどの人は使う必要はないです.通常の利用ではデフォルトのスレッドベースで十分でしょう.少し凝ったことがしたい,例えばスレッドプールを使ったり,タスクに独自の優先順位をつけたい,といった状況であれば独自のSchedulerを実装することになります.
D言語で広く使われているイベント駆動なWebフレームワークvibe.dは,以下のような実装をすでにmasterで持ってます(github上でのコード).
package class VibedScheduler : Scheduler
{
override void start(void delegate() op) { op(); }
override void spawn(void delegate() op) { runTask(op); }
override void yield() {}
override @property ref ThreadInfo thisInfo() { return Task.getThis().tidInfo; }
override TaskCondition newCondition(Mutex m) { return new TaskCondition(m); }
}
vibe.dの場合にはイベント駆動IOとの兼ね合いもあるので,既存のスケジューラは使わずに,独自のタスクや条件変数を使うようにしています.今までは外側からこれと近いことをしてましたが,スケジューラと結合することで,かなり実装がシンプルになっています.
独自スケジューラを実装したい場合には,標準組み込みの二つのスケジューラや,外部ライブラリの実装を参照して見てると良いと思います.
まとめ
まだ正式にリリースされていない機能ですが,次のバージョンから使えるということで,std.concurrencyのSchedulerを紹介しました.std.concurrencyは改善が活発なモジュールの一つでもあるので,また何か変更があれば随時記事にして行けたらなと思います.
また,一緒に入ったGeneratorというクラスについても,時間を見つけて紹介する予定です.