次の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
というクラスについても,時間を見つけて紹介する予定です.