非同期実行
4D では 同期的 および 非同期 な実行モードの両方をサポートしており、これによりデベロッパーがパフォーマンス、レスポンス、作業負荷分散に基づいて最適なアプローチを選択することができます。
基本
同期的実行
同期実行は シーケンシャル なフローに従います。これはそれぞれの指示が、次の指示が始まるまでに完了するというステップ・バイ・ステップ方式です。 これはつまりオペレーションが完了するまで実行スレッドがブロックされるということを意味します。
同期的実行は以下のような場合で使用されます:
- タスクの実行が厳密な順番に従う必要があるとき。
- パフォーマンスへの影響が最小限である(例: 素早いオペレーション)。
- ブロッキングが許容可能な、シングルスレッドでのコンテキストで実行される。
- 同期実行はUI をブロックするため、ブロックが起きても許容され得る、素早く順序付けされたタスクに対して適しています。
非同期実行
非同期実行はイベント駆動型であり、タスクを実行中でも他の操作を完了させることができます。 これは実行フローを管理するために、 コールバック、ワーカー、および イベントハンドラ といったものに依存します。
非同期実行は以下のような場合で使用されます:
- 操作が長時間にわたる(例: サーバーのレスポンスを待つなど)。
- レスポンシブネスの良さが重要である場合(例: UI インタラクションなど)。
- バックグラウンド処理、ネットワーク通信、あるいは並列処理などを実行する場合。
同期的実行と非同期実行のどちらを選んだら良いかについては、以下の表をご覧下さい:
| シナリオ | 最適なアプローチ |
|---|---|
| 最小限の処理とクイックなオペレーション | 同期的実行 |
| 厳密な順番に従う必要があるタスク | 同期的実行 |
| 長時間にわたるバックグラウンド処理 | 非同期実行 |
| 長時間にわたるUI インタラクション処理 | 非同期実行 |
| 短時間のUI インタラクション処理 | 同期的実行 |
| 高パフォーマンスが必要な、マルチスレッドワークロード | 非同期実行 |
基本原理
4D はさまざまなクラスやコマンドを通して、ビルトインの非同期実行機能を提供します。 これらを使用することで、カレンとプロセスをブロックすることなく、他のオペレーションが完了するのを待ちながら、バックグラウンド処理、ネットワーク通信、そして大量のデータ処理などを行うことができます。
4D における非同期イベントの管理の一般的な概念は、ワーカー(イベントをリッスンするプロセス)およびコールバック(あるイベントが発生した際に自動的に実行される関数またはフォーミュラ)を使用した非同期メッセージモデルに基づいています。 ここでは何かの結果を待つ(同期モード)のではなく、特定のイベントが発生した際に自動的に呼び出される関数を提供します。 コールバックはクラス関数(推奨)またはフォーミュラオブジェクトとして渡すことができます。
This model is common to CALL WORKER, CALL FORM, and classes that support aynchronous execution. これらのコマンド/クラスは全て、バックグラウンドで実行されるオペレーションを開始します。 オペレーションを開始するステートメントは、オペレーションが終わるのを待たずに即座に戻ります。
ワーカー
非同期プログラミングは ワーカー (ワーカープロセス) というシステムに依存しています。これを使用することでメインプロセスをブロックすることなく、コードを実行することができます。 これは特に、インターフェースをレスポンシブな状態にしたまま、長時間にわたるタスク(HTTP 呼び出し、外部プロセスの実行、バックグラウンド処理など)を処理するのに有効です。
非同期プログラミングにおいてワーカープロセスの使用は必須です。いわゆる"クラシック"なプロセスはプロセスメソッドが終了した時に実行を自動的に終了するため、コールバックを使用するようなことができないからです。 ワーカープロセスであればその後も生き続け、イベントをリッスンすることができます。
イベントキュー(メールボックス)
Each worker (or form window for CALL FORM) has its own message queue. CALL WORKER or CALL FORM simply posts a message to this queue. ワーカーは、独自のコンテキスト内において、メッセージを一つずつ受信した順番で管理していきます。 プロセス変数、カレンとレクション、などは保持されます。
メッセージを介した双方向通信
呼び出しプロセスがメッセージを投稿すると、ワーカーはそれを実行します。 The worker can in turn post a message (via CALL WORKER or CALL FORM) back to the caller or another worker to notify an event (task completion, data received, error, progress, etc.). この機構により、クラシックな同期呼び出しの応答を置き換えることができます。
イベントリスニング
イベント駆動型の開発において、一部のコードが、入ってくるイベントを聞ける(リッスンできる)状態でなければならい事は明らかです。 イベントは、ユーザーインターフェース(オブジェクトのマウスクリックやキーボードのキーが押されたなど)や、HTTP リクエストや他のアクションの完了などのその他のインタラクションによって生成され得ます。 例えば、フォームがDIALOG コマンドを使用して表示されている場合、ユーザーアクションによってイベントがトリガーされ、それをコードで処理することが可能です。 ボタンをクリックした場合はボタンに割り当てられたコードがトリガーされることになります。
非同期実行のコンテキストにおいては、以下の機能がリスニングモード内でコードを配置します:
CALL WORKERexecutes the code for which it has been called, then returns to a listening status from where it can be called afterwards.CALL FORMopens a form and makes it listen for incoming messages from the event queue.wait()を呼び出すと、他のインスタンスからのコールバック内のterminate()あるいはshutdown()をリッスンします。
イベントのトリガー
イベントは実行フローの間に自動的にトリガーされ、対応するコールバックへと渡されます。 wait() の途中に terminate() あるいは shutdown() を呼び出すことで、強制的にイベントをトリガーさせることもできます。
コールバック実行コンテキスト
4D がコールバックを実行する時、それをカレントプロセス(ワーカー)のコンテキストにおいて実行します。つまり、例えばオブジェクトがフォーム内でインスタンス化された場合、コールバックもその同じフォームのコンテキスト内で実行されるということです。
コールバックが適切に非同期モードで実行されるためには、オペレーションは一般的に、ワーカーから(CALL WORKER 経由で)ローンチされる必要があります。 UI を管理しているプロセスからローンチした場合、UI がイベントをリッスンできる状態になるまで一部のコールバックが呼び出されない可能性があります。
非同期オブジェクトのリリース
4D では、全てのオブジェクトは、メモリ内に そのオブジェクトへの参照がもう残っていない 場合にそのオブジェクトがリリースされます。 これは一般的に、メソッド実行の最後にローカル変数が消去される時に発生します。
非同期クラスにおいては、オブジェクトをインスタンス化したプロセス内において 追加の参照 が必ず4D によって維持されています。 この参照はオペレーションが完了したときにのみリリースされます。つまり、 onTerminate イベントがトリガーされたあとです。 この自動参照によって、変数から特別に参照していなくても、オブジェクトを最後まで存続させることができます。
オブジェクトを任意のタイミングで"強制的に"リリースしたい場合、.shutdown() あるいは terminate() 関数を使用します: これらはonTerminate イベントをトリガーするため、オブジェクトはリリースされます。
共通した概念を示した表
| 機能 | 非同期のローンチの方法 | コールバック / イベントの管理 |
|---|---|---|
| CALL WORKER | CALL WORKER("wk"; "MyMethod"; $params) | MyMethod は $params の引数を渡して呼び出されます |
| CALL FORM | CALL FORM($win; "MyMethod"; $params) | MyMethod は $params の引数を渡して呼び出されます |
| 4D.SystemWorker | 4D.SystemWorker.new(cmd; $options) | コールバック: onData、onResponse、onError、onTerminate |
4Dクラスによる非同期プログラミング
複数の4D クラスが非同期処理をサポートしています:
HTTPRequest– 非同期のHTTP リクエストやレスポンスを管理します。SystemWorker– 外部プロセスを非同期に実行します。TCPConnection– TCP クライアント接続をイベント駆動型のコールバックで管理します。TCPListener– TCP サーバー接続を管理します。UDPSocket– UDP パケットを送信し受信します。WebSocket– WebSocket クライアント接続を管理します。WebSocketServer– WebSocket サーバー接続を管理します。
これらのクラスは非同期実行に関しては同じルールに従います。 これらのクラスのコンストラクターは、非同期オブジェクトを設定するために使用される options 引数を受付ます。 この場合の options オブジェクトには、コールバック関数を備えたユーザークラス インスタンスであることが推奨されます。 例えば、クラス内に onResponse() 関数を作成した場合、reponse イベントが発生した際にそれが自動的に非同期で呼び出されます。
以下のような手順が推奨されます:
- コールバック関数を宣言するユーザークラスを作成します。例えば、
onError()およびonResponse()関数を持つ、cs.Paramsクラスなどです。 - そのユーザークラスをインスタンス化し(ここでの例では
cs.Params.new()クラスを使用)、それを使用して非同期オブジェクトを設定します。 - 4D クラスのコンストラクターを呼び出し(例えば
4D.SystemWorker.new()など)、options オブジェクトを引数として渡します。 渡されたオペレーションは、遅延なくすぐに開始されます。
以下は、ユーザークラスに基づいた options オブジェクトの実装の完全な一例です:
// 非同期コード作成
var $options:=cs.Params.new(10) // cs.Params クラスのコードについては以下参照
var $systemworker:=4D.SystemWorker.new("/bin/ls -l /Users ";$options)
// "Params" クラス
Class constructor ($timeout : Real)
This.dataType:="text"
This.data:=""
This.dataError:=""
This.timeout:=$timeout
Function onResponse($systemWorker : Object)
This._createFile("onResponse"; $systemWorker.response)
Function onData($systemWorker : Object; $info : Object)
This.data+=$info.data
This._createFile("onData";this.data)
Function onDataError($systemWorker : Object; $info : Object)
This.dataError+=$info.data
This._createFile("onDataError";this.dataError)
Function onTerminate($systemWorker : Object)
var $textBody : Text
$textBody:="Response: "+$systemWorker.response
$textBody+="ResponseError: "+$systemWorker.responseError
This._createFile("onTerminate"; $textBody)
Function _createFile($title : Text; $textBody : Text)
TEXT TO DOCUMENT(Get 4D folder(Current resources folder)+$title+".txt"; $textBody)
ここでonResponse、 onData、 onDataError および onTerminate 関数は、4D.SystemWorker によってサポートされている関数であるという点に注意してください。
ユーザークラスがインスタンス化されたら、4D はevent listening モードになり、4D はイベントをトリガー することで、ユーザークラス内の対応する関数を呼び出すことができます。
一部の場合においては、クラス関数の代わりに、プロパティ値としてフォーミュラを使用したい場合があるかもしれません。 これはベストプラクティスではありませんが、以下のようなシンタックスがサポートされています:
var $options.onResponse:=Formula(myMethod)
非同期コード内での同期的な実行
現代的な非同期コードを使用している場合でも、ある程度の同期実行が必要となる場合があるかもしれません。 例えば、ある関数が結果を得るまで、ある程度の時間待つようにしたいかもしれません。 これは例えば、保証された速いネットワーク接続や、システムワーカーなどが考えられます。 このような場合、wait() 関数を使用することで、同期的な実行を強制することができます。
.wait() 関数はカレントプロセスの実行を一時停止させ、4D をイベントリスニング モードにします。 ただし、これは wait() 関数が呼ばれたオブジェクトからだけでなく、どのソースから受信したイベントであってもトリガーされるという点に注意してください。
wait() 関数は、onTerminate イベントがオブジェクト上でトリガーされた場合か、あるいは指定されたタイムアウト(あれば)が経過した場合に実行を返します。 結果として、コールバック内から shutdown() あるいは terminate() を呼び出すことで、.wait() から明示的に抜け出すことができます。 それ以外の場合は、 .wait() はカレントのオペレーションが終了した際に終了します。
例:
var $options:=cs.Params.new()
var $systemworker:=4D.SystemWorker.new("/bin/ls -l /Users ";$options)
$systemworker.wait(0.5) // ファイル情報を取得するまで0.5 秒まで待機します
参照
Blog post: Launch an external process asynchronously
Asynchronous Call