スレッド†
6 個の関数。
THREADSTART†
書式†
THREADSTART( name [, arg ... ] )
引数†
| Parameter | Description |
|---|---|
| name | 別スレッドで実行するユーザー関数の名前(文字列) |
| arg | 関数に渡す引数。省略できる。複数渡せば _argv[0]、_argv[1]、…になる |
戻り値†
スレッドの ID(1 以上の整数)。起動できなかったときは負の値。
| 値 | 意味 |
|---|---|
| 1 以上 | 起動した。以後の THREAD* 関数にこの ID を渡す |
| -1 | その名前の関数がない(警告 W0035) |
| -2 | 同時に動かせる数(32)を超えた |
| -3 | unload の処理中で、新しいスレッドは作れない |
| -4 | OS がスレッドを作れなかった |
| -5 | このビルドはスレッドに対応していない(Emscripten) |
引数が足りない・文字列でないときは、警告 W0008 / W0009 を出して -1 を返します。
解説†
指定した関数を別のスレッドで動かします。SSP とのやりとり(request)とは別に、時間のかかる処理を並行して進めたいときに使います。呼び出した側はスレッドの終わりを待たずにすぐ戻ります。
OnBoot
{
work.done = 0
work.id = THREADSTART('Work.Heavy', 1000000)
'計算を始めました'
}
Work.Heavy
{
_sum = 0
for _i = 0; _i < _argv[0]; _i++ {
_sum += _i
}
work.sum = _sum // 結果はグローバル変数に書いておく
work.done = 1
}
OnSecondChange
{
if work.done {
'計算が終わりました: ' + TOSTR(work.sum)
}
}
スレッドの結果の受け取り方は 3 通りあります。
- 関数の返り値を THREADRESULT で受け取る
- グローバル変数に書いて終わる(上の例)。ほかのスレッドとグローバル変数は共有です
- DIRECTSSTP で SSP へ知らせる。ワーカースレッドから呼ぶと、通信を待っている間は他の処理を止めません
動き方†
- グローバル変数は共有、ローカル変数は独立です。スレッドの関数は自分のローカル変数を持ちます
- 辞書の関数を同時に実行するのは 1 つのスレッドだけです。ワーカーは文の切れ目で実行を譲り合い、SSP からの
requestは優先して実行されます。ワーカーが動いていても、requestはワーカーの次の文の切れ目まで待つだけで返ってきます。複数の CPU を使って計算を速くするものではありません - 外部の遅い応答を待つ間は、他のスレッドが止まりません。ワーカーから呼んだ REQUESTLIB(SAORI)、EXECUTE_WAIT、EXECUTE_CAPTURE、DIRECTSSTP、SLEEP は、待つ間に実行を譲ります。THREADJOIN も同じです。SSP の
requestの中で呼んだREQUESTLIB/EXECUTE_WAITも、動いているワーカーがあれば、待つ間にワーカーが動けます(SLEEPは、requestの中ではこれまでどおり待ちます)。これらの待ち以外のシステム関数(大きなファイル処理など)は、1 回の呼び出しが終わるまで実行を譲ることも中断することもできません - 同時に動かせるのは 32 本までです。終わったスレッドの記録は、THREADCLOSE で捨てるか、256 件を超えると古いものから消えます
- スレッドは起動した時点の関数を使います。関数の定義(中間コード)は、SSP からの
requestと全てのスレッドで共有します。式を計算している途中の値は、関数を呼び出すたびに別に持つので、同じ関数を複数のスレッドが同時に実行してもかまいません(自分自身を再帰で呼んでも同じです)。スレッドを起動するときに辞書の大きさに応じたメモリや時間はかからないので、同時に何本動かしても、増えるのはそのスレッドが使う分だけです(2 万関数・2.5MB の辞書で、起動に約 2ms、ワーカー 1 本あたりのメモリ増加は 0.1MB 未満) fncdepthとlooplimitの値は起動時の設定が引き継がれます
制限†
- ワーカーが動いている間は、辞書や関数を変える DICLOAD、DICUNLOAD、APPEND_RUNTIME_DIC、UNDEFFUNC は警告 W0032 を出して失敗します。EVAL は使えます
- ワーカーが出した警告やエラーのログは、ファイルにはその場で書かれますが、SSP やチェックツールへは次の
requestの中でまとめて送られます(SHIORI のコールバックは、requestを呼んだスレッドで呼ぶ約束のため) - ワーカーの中の
GETLASTERROR、LSO、RE_GETSTRなどは、スレッドごとに保たれます
unload と中断†
unload が来ると、動いているワーカーすべてに中断を求め、5 秒まで終わるのを待ちます。ワーカーは次の文の切れ目で、return と同じ形で関数を抜けます(SLEEP の待ちはすぐ戻ります)。それでも終わらないスレッド(時間のかかるシステム関数の中など)は、最後の手段として強制停止し、警告 W0034 を出します。Windows 以外では強制停止ができないため、通信などで待っているスレッドは見捨てて進みます(辞書の関数を実行中のスレッドは、終わるまで待ち続けます)。
ワーカーで長く止まる関数を使わない
強制停止はスレッドの途中の状態を壊しうる手段です。時間のかかる処理は、for / while で小さく区切るか、SLEEP を挟んで書いてください。そうすれば THREADSTOP や unload で安全に止まります。
使用例†
2 つのスレッドを起動して、両方の終わりを待ち、返り値を受け取ります。
Work.Add
{
_argv[0] + _argv[1]
}
Work.Both
{
_a = THREADSTART('Work.Add', 1, 2)
_b = THREADSTART('Work.Add', 10, 20)
_r = THREADJOIN(_a)
_r = THREADJOIN(_b)
TOSTR(THREADRESULT(_a)) + ',' + TOSTR(THREADRESULT(_b)) // 3,30
}
互換性†
- Tc608-1以降
- Tc608-2: スレッドごとの関数表のコピーをやめ、全スレッドで共有するようにした(Tc608-1 では、2 万関数・2.5MB の辞書で、起動に約 18ms、ワーカー 1 本あたり約 13MB かかっていた)。同じ関数を複数のスレッドが同時に実行できる
関連項目†
THREADSTATE†
書式†
THREADSTATE( id )
引数†
| Parameter | Description |
|---|---|
| id | THREADSTART が返したスレッドの ID |
戻り値†
スレッドの状態を表す整数。
| 値 | 意味 |
|---|---|
| -1 | その ID のスレッドがない(THREADCLOSE で捨てた、または古い記録が消えた) |
| 0 | 実行中(実行の順番待ちを含む) |
| 1 | 関数を最後まで実行して終わった |
| 2 | 中断された(THREADSTOP、THREADCLOSE、unload) |
| 3 | エラーで終わった(強制停止したスレッドを含む) |
引数が足りない・整数でないときは、警告 W0008 / W0009 を出して -1 を返します。
解説†
スレッドが動いているか、終わったかを調べます。待たずにすぐ戻るので、OnSecondChange などで繰り返し確かめる使い方に向きます。終わるまで待ちたいときは THREADJOIN を使います。
SSP の応答を止めないために
ホストの request の中で THREADJOIN を長く待つと、SSP の応答が止まります。SSP に返す処理では、THREADSTATE で確かめて、終わっていなければ次の機会に回してください。
使用例†
OnSecondChange
{
if THREADSTATE(work.id) == 1 {
'終わりました: ' + TOSTR(THREADRESULT(work.id))
}
}
互換性†
- Tc608-1以降
関連項目†
THREADSTOP†
書式†
THREADSTOP( id )
引数†
| Parameter | Description |
|---|---|
| id | THREADSTART が返したスレッドの ID |
戻り値†
| 値 | 意味 |
|---|---|
| 1 | 中断を求めた |
| 0 | そのスレッドはすでに終わっている |
| -1 | その ID のスレッドがない |
引数が足りない・整数でないときは、警告 W0008 / W0009 を出して -1 を返します。
解説†
スレッドに中断を求めます。終わるのを待たずにすぐ戻ります。終わりを確かめるには THREADSTATE か THREADJOIN を使います。
スレッドは次の文の切れ目(while / for の周回の切れ目を含む)で、return と同じ形で今の関数を抜け、呼び出し元の関数も同じ形で抜けて、スレッドが終わります。SLEEP や THREADJOIN で待っているスレッドは、待ちを途中でやめて戻ります。中断されたスレッドの状態は 2 になり、THREADRESULT は空の値を返します。
システム関数 1 回の呼び出しの途中(大きな正規表現、REQUESTLIB など)では止まらず、その呼び出しが終わってから止まります。
使用例†
OnClose
{
_r = THREADSTOP(work.id)
_r = THREADJOIN(work.id, 2000) // 2 秒まで終わりを待つ
}
互換性†
- Tc608-1以降
関連項目†
THREADJOIN†
書式†
THREADJOIN( id [, timeout ] )
引数†
| Parameter | Description |
|---|---|
| id | THREADSTART が返したスレッドの ID |
| timeout | 待つ時間の上限(ミリ秒)。省略すると終わるまで待つ |
戻り値†
| 値 | 意味 |
|---|---|
| 1 | スレッドが終わった(すでに終わっていた場合を含む) |
| 0 | 時間切れで、まだ終わっていない |
| -1 | その ID のスレッドがない |
| -2 | 自分自身は待てない |
引数が足りない・整数でないときは、警告 W0008 / W0009 を出して -1 を返します。
解説†
スレッドが終わるまで待ちます。待っている間、ほかのスレッドは動けます。待っている側のスレッドが中断を求められたときも、待ちをやめて戻ります。
終わったあとの返り値は THREADRESULT で受け取ります。
ホストの request の中で長く待たない
SSP からの request の中で THREADJOIN を長く待つと、その間 SSP の応答が止まります。SSP のウィンドウの処理が止まるだけでなく、そのスレッドから送ろうとした通信(ワーカーが呼んだ DIRECTSSTP など)は、SSP が応答できず時間切れになります。短い timeout を付けるか、THREADSTATE で確かめる使い方にしてください。
使用例†
_id = THREADSTART('Work.Add', 3, 4)
_r = THREADJOIN(_id, 1000)
if _r == 1 {
THREADRESULT(_id) // 7
}
互換性†
- Tc608-1以降
関連項目†
THREADRESULT†
書式†
THREADRESULT( id )
引数†
| Parameter | Description |
|---|---|
| id | THREADSTART が返したスレッドの ID |
戻り値†
スレッドの関数の返り値。
次の場合は空の値(VOID)を返します。
- スレッドがまだ動いている
- 中断された・エラーで終わった(THREADSTATE が 2 か 3)
- その ID のスレッドがない
引数が足りない・整数でないときは、警告 W0008 / W0009 を出して空の値を返します。
解説†
正常に終わったスレッドの関数が返した値を受け取ります。関数が複数の候補を持つとき(ランダムに選ばれる出力)は、終わるときに選ばれた 1 つです。
返り値を受け取らずに、グローバル変数へ書く方法もあります。THREADSTART を参照してください。
使用例†
_id = THREADSTART('Work.Add', 3, 4)
_r = THREADJOIN(_id)
THREADRESULT(_id) // 7
互換性†
- Tc608-1以降
関連項目†
THREADCLOSE†
書式†
THREADCLOSE( id )
引数†
| Parameter | Description |
|---|---|
| id | THREADSTART が返したスレッドの ID |
戻り値†
| 値 | 意味 |
|---|---|
| 0 | 終わっているスレッドの記録を捨てた |
| 1 | まだ動いているので中断を求めた。終わったら記録を捨てる |
| -1 | その ID のスレッドがない |
引数が足りない・整数でないときは、警告 W0008 / W0009 を出して -1 を返します。
解説†
スレッドの記録(状態と返り値)を捨てます。動いているスレッドには THREADSTOP と同じ中断を求め、終わった時点で記録を捨てます。捨てたあとは、その ID を THREADSTATE などに渡すと -1 になります。
捨てなくても、終わったスレッドの記録は 256 件を超えると古いものから消えます。結果を使い終わったスレッドは、捨てておくと記録が残りません。
使用例†
_id = THREADSTART('Work.Add', 3, 4)
_r = THREADJOIN(_id)
_v = THREADRESULT(_id)
_r = THREADCLOSE(_id)
互換性†
- Tc608-1以降