別スレッドでタスクを実行する機能としてBackgroundTaskQueueクラスが提供されます。BackgroundTaskQueueクラスで定義されるasync-start-taskプロシージャは、サブアプレットの中でコードを実行し、その結果を取得します。UIとの対話処理をブロックしないように、異なるスレッドでコードを実行することができます。これによりマルチコアやハイパースレッディングを含む最近のCPUのパワーのアドバンテージを利用することができるでしょう。
サンプル
http://developers.curlap.com/curl/curl-ext/hp/BackgroundTas/sample/start.curl
このサンプルアプレットは、上図のようなマンデルブロ集合と呼ばれるフラクタル図形を表示します。フラクタル図形は細部を拡大しても無限に自己相似な図形が現れ続けるという興味深い性質を持つ図形です。このアプレットの表示は左クリックで拡大、Shift+左クリックで縮小しますのでその様子を確認してみてください。
この図形は細部を描写するために時間のかかる計算を必要とします。このアプレットでは画面を128ピクセルの正方形で分割して、それぞれの領域の演算を複数のスレッドで同時に行っていきます。計算の終わったスレッドが算出した領域から順次表示していきますので、必ずしも左上から一領域ずつ順番に描写されない点、また、描画途中でも拡大、縮小のクリック操作を受け付ける点に注目してください。
使い方
start.curlのredrawメソッド内の以下の部分でBackgroundTaskQueue.async-start-taskの機能が使用されています。
{for x-index = 0 below self.x-pixels step self.step-value do set y0 = self.min-y {for y-index = 0 below self.y-pixels step self.step-value do def x = x-index def y = y-index || 演算処理をキューに入れ る {BackgroundTaskQueue.async-start-task "calculate-rectangle", {Arguments x0, y0, increment, self.step-value}, package-selector = {make-package-selector "COM.CURL.EXT.BACKGROUND-TASK.MANDLEBROT-SAMPLE", location = {url "mand.scurl"} }, queue = self.queue, {on e:BackgroundTaskEvent do ||ズームのためにマウスがクリックされてタスクがキャンセルされるとき e.canceled? は true。 {if e.exception == null and not e.canceled? then || タスク終了 {dec self.working-tasks} || {Array-of bool} がこの正方形の領域に関する計算結果 def r = e.result[0] asa {Array-of bool} || 演算結果に従ってPixmapを描画 {mutate-fill-pattern p:Pixmap on self.fill-pattern do let r-index:int {for i = 0 below self.step-value do {for j = 0 below self.step-value do {p.set x + i, y + j, {if r[r-index] then {Pixel.from-uint8 0, 0, 0} else {Pixel.from-uint8 0xff, 0xff, 0xff} } } {inc r-index} } } } } } } || タスクを一つキューに入れた {inc self.working-tasks} set y0 = y0 + (increment * self.step-value) } set x0 = x0 + (increment * self.step-value) } |
BackgroundTaskQueueクラスのasync-start-taskプロシージャが二重ループの中で呼び出されています。そのたびごとにcalculate-rectangleプロシージャを呼び出すタスクがキューに追加され、可能ならば実行されます。calculate-rectangleプロシージャには128ピクセル四方の正方形領域の演算が行われます。
BackgroundTaskQueue.async-start-taskプロシージャの引数の定義は以下のようになっています。
||実行するプロシージャ、あるいはコンストラクタを実行するクラスの名前。 proc-name:String ||proc-nameに渡す引数。 args:Arguments ||proc-nameを探すパッケージをインポートするために使われるパッケージセレクタ(デフォルト:null) package-selector:#ComponentSelector ||package-selectorがパッケージを探すために使うマニフェスト。 ||指定されない場合はmanifest-urlとroot-manifest-urlからの値が使用される。 ||どちらも未指定の場合はカレントプロセスのデフォルトマニフェストが使用される。 manifest:#ComponentManifest = {get-process-manifest}, ||必要があれば、メインマニフェストのURL(デフォルト:null) manifest-url:#Url ||必要があれば、ルートマニフェストのURL(デフォルト:null) root-manifest-url:#Url ||このタスクを開始するために使用されるBackgroundTaskQueue。 queue:BackgroundTaskQueue = {BackgroundTaskQueue.get-default-background-task-queue}, ||BackgroundTaskEventを受け取るイベントハンドラ。 event-handler:EventHandler …:EventHandler |
このサンプルではBackgroundTaskQueue.async-start-taskプロシージャのqueue引数にself.queueが渡されており、その値は以下のように記述されています。
field queue:BackgroundTaskQueue = {BackgroundTaskQueue max-threads = 8, max-idle-threads = 8} |
BackgroundTaskQueueクラスのコンストラクタ引数定義は以下のようになっています。
{default ||同時にアクティブになる最大スレッド数(デフォルト:4) max-threads:int ||同時にアイドル状態になる最大スレッド数(デフォルト:0) max-idle-threads:int ||BackgroundTaskRemoteInterfaceの呼び出しに応えるサブアプレットのURL async-task-applet-url:Url = {BackgroundTaskQueue.default-async-task-applet-url} |