ここは旧式の4DドキュメントWebサイトです。最新のアップデートされたドキュメントを読むには新サイトをご利用下さい→ developer.4d.com |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
4D v19.8
プリエンプティブ4Dプロセス
|
プリエンプティブ実行 | |
4D Server | ○ |
4D リモート | ○ (ServerNet 使用時のみ、新しい ServerNet ネットワークレイヤー(互換性)参照) |
4D シングルユーザー | ○ |
コンパイル済みモード | ○ |
インタープリターモード | - |
実行コンテキストがプリエンプティブモードをサポートし、かつメソッドが "スレッドセーフ" である場合、New process あるいは CALL WORKERコマンドあるいは "メソッドを実行" メニュー項目を使用してローンチされた新しい 4Dプロセスは、プリエンプティブスレッド内にて実行されます。
それ以外の場合で、サポートされていない実行コンテキスト (例えばインタープリタモードなど) から New process あるいは CALL WORKER コマンドを呼び出した場合、プロセスは常にコオペラティブに実行されます。
4Dコードは、いくつかの特定の条件に合致していた場合に限りプリエンプティブスレッド内で実行することができます。実行コードのそれぞれの部分 (コマンド、メソッド、変数など) がプリエンプティブ実行に準拠している必要性があります。プリエンプティブスレッドで実行可能な要素はスレッドセーフと呼ばれ、プリエンプティブスレッドで実行できない要素はスレッドアンセーフと呼ばれます。
注: スレッドは親プロセスメソッドをスタートとして独自に管理されているので、呼び出しチェーン全体のどこにおいてもスレッドアンセーフなコードが含まれていてはいけません。そのようなコードが含まれていた場合、プリエンプティブに実行することはできません。この点については、プロセスがプリエンプティブに実行される条件とは? の章で詳細な説明があります。
それぞれの "スレッドセーフティ" プロパティは、要素自身によります:
原則として、プリエンプティブスレッド内で実行されるコードは外部との相互作用する部分、例えばプラグインコードやインタープロセス変数などを呼び出すことはできません。しかしながら 4Dデータサーバーはプリエンプティ実行をサポートしていることから、データアクセスは可能です。
デフォルトでは、4Dはすべてのプロジェクトメソッドをコオペラティブモードで実行します。プリエンプティブモードを利用したい場合は、まず最初に、可能な限りプリエンプティブモードで開始したいメソッドをすべて明示的に宣言することから始まります。これはつまり、プリエンプティブプロセスで実行可能なメソッドであるということです。コンパイラーは、それらのメソッドが実際にスレッドセーフであるかどうかをチェックします (詳細な情報については スレッドセーフなメソッドの書き方 を参照してください)。また、必要であれば、一部のメソッドに対してプリエンプティブモードを禁止することもできます。
プリエンプティブで使用可能 ("capable") であると宣言することは、当該メソッドにプリエンプティブ実行の資格を与えますが、実行時にそのメソッドが実際にプリエンプティブモードで実行されることを保障するものではないことに留意が必要です。プロセスをプリエンプティブモードで開始することは、プロセス内の呼び出しチェーン内のすべてのメソッドの、関連プロパティを4Dが評価して初めて可能になります (より詳細な情報については、プロセスがプリエンプティブに実行される条件とは? の段落を参照してください)。
メソッドがプリエンプティブモードに則していることを宣言するためには、メソッドプロパティダイアログボックスの実行モード宣言オプションを使用する必要があります:
以下のオプションが提供されています:
例えば METHOD GET CODE を使用して、メソッドのコードを書き出す場合、"プリエンプティブ" プロパティは "capable"、あるいは "incapable" の値で "%attributes" コメント内に書き出されます ("indifferent" オプションを選択している場合には、このプロパティは提供されません)。METHOD GET ATTRIBUTES および METHOD SET ATTRIBUTES コマンドも、"プリエンプティブ" 属性の値 ("capable", "incapable", "indifferent") を取得、あるいは指定します。
以下の表はプリエンプティブモードの宣言オプションをまとめたものです:
オプション | プリエンプティブプロパティ値 (インタープリター) | コンパイラーの挙動 | 内部タグ (コンパイル済み) | 呼び出しチェーンがスレッドセーフだった際の実行モード |
プリエンプティブプロセス内で実行可能 | capable | 資格をチェックし、不可能だった場合にはエラーを返します | スレッドセーフ | プリエンプティブ |
プリエンプティブプロセス内で実行不可能 | incapable | 評価しません | スレッドアンセーフ | コオペラティブ |
4Dに判断させる | indifferent | 評価しますが、エラーを返しません | スレッドセーフあるいはスレッドアンセーフ | スレッドセーフの場合: プリエンプティブ; スレッドアンセーフの場合: コオペラティブ; 直接呼びされた場合: コオペラティブ |
リマインダー: プリエンプティブ実行はコンパイル済みモードでのみ利用可能です。
コンパイル済みモードでは、New process あるいは CALL WORKER メソッドで作成されたプロセスを開始するとき、4Dはプロセスメソッド (別名親メソッド) のプリエンプティブプロパティを読み、そのプロパティに応じてプロセスをプリエンプティブモードあるいはコオペラティブモードで実行します:
実際のスレッドセーフプロパティは呼び出しチェーンによります。"capable" と宣言されたプロパティを持つメソッドが、スレッドアンセーフなメソッドをサブレベル (どちらでも) で呼び出した場合、コンパイルエラーが返されます。呼び出しチェーン全体の中で一つでもメソッドがスレッドアンセーフであれば、それは他のすべてのメソッドをいわば "汚染" し、プリエンプティブ実行はコンパイラーによって拒否されます。プリエンプティブスレッドは、呼び出しチェーン全体がスレッドセーフであり、プロセスメソッドが "プリエンプティブプロセスで実行可能" と宣言されていた場合にのみ作成可能です。
その一方で、同じスレッドセーフメソッドを、呼び出しチェーン内ではプリエンプティブスレッド内で実行し、他の呼び出しチェーン内ではコオペラティブスレッド内で実行することが可能です。
例えば、次のプロジェクトメソッドの場合:
// MyDialog プロジェクトメソッド
// インターフェースコールを含み、内部的にスレッドアンセーフです
$win:=Open window("tools";Palette form window)
DIALOG("tools")
// MyComp プロジェクトメソッド
// 単純な演算を含み、内部的にスレッドセーフです
C_LONGINT($1)
$0:=$1*2
// CallDial プロジェクトメソッド
C_TEXT($vName)
MyDialog
// CallComp プロジェクトメソッド
C_LONGINT($vAge)
MyCom($vAge)
プリエンプティブモードでのメソッド実行は、"プリエンプティブ" プロパティや呼び出しチェーンに依存します。
以下の表は、これらの様々な状況をまとめたものです:
宣言と呼び出しチェーン | コンパイル | スレッドセーフの結果 | 実行モード | 補足 |
![]() | OK | ![]() | プリエンプティブ | CallComp は親メソッドで、プリエンプティブな使用が "capable"(可能) と宣言されています。MyComp は内部的にスレッドセーフなので、CallComp も内部的にスレッドセーフとなり、プロセスはプリエンプティブになります |
![]() | エラー発生 | ![]() | 実行不可能 | CallDial は親メソッドでプリエンプティブ "capable" (可能)、MyDialog は "indifferent" と宣言されています。しかし、MyDialog が内部的にはスレッドアンセーフのため、呼び出しチェーンを "汚染" してしまいます。CallDial の宣言と実際の実効性が矛盾するためコンパイルは失敗します。解決方法は、MyDialog を変更してスレッドセーフにして実行をプリエンプティブにするか、CallDial のプロパティを変更してコオペラティブに実行するようにします。 |
![]() | OK | ![]() | コオペラティブ | CallDial はプリエンプティブな使用が "incapable"(不可) と宣言されているのでコンパイル時には内部的にスレッドアンセーフとなり、MyDialog の状況に関わらず実行はかならずコオペラティブになります。 |
![]() | OK | ![]() | コオペラティブ | CallComp が親メソッドでプロパティが "indifferent" のため、呼び出しチェーンがすべてスレッドセーフでも、プロセスはコオペラティブになります。 |
![]() | OK | ![]() | コオペラティブ | CallDial が親メソッドでプロパティが "indifferent" のため、プロセスはコオペラティブになり、コンパイルは成功します。 |
4Dではプロセスに対してコオペラティブ実行かプリエンプティブ実行かを識別する新機能を提供しています:
プロセス型 | アイコン |
プリエンプティブストアドプロシージャ | ![]() |
プリエンプティブワーカープロセス | ![]() |
プリエンプティブWeb メソッド | ![]() |
プリエンプティブSOAP メソッド | ![]() |
スレッドセーフであるためには、メソッドは以下のルールに従う必要があります:
注:
(*) プリエンティブプロセス間でデータを交換するためには、共有コレクションあるいは共有オブジェクトを引数としてプロセスに渡すか、Storage カタログを使用することができます(これは全てのプロセス間でのデータ交換でも使用可能です)。詳細な情報については、共有オブジェクトと共有コレクション のページを参照してください。
ワーカープロセスという新種のプロセスによって、プリエンプティブプロセスを含むあらゆるプロセス間でデータの交換ができるようになります。より詳細な情報に着いてはワーカーについてを参照して下さい。
(**) 新しいClientComment コマンドを使用すると、プリエンプティブプロセスからインターフェースオブジェクトを呼び出せるというスマートなソリューションが可能になります。
"プリエンプティブプロセスで実行可能"プロパティを持つメソッドは、コンパイル時に4Dによってチェックされます。メソッドがスレッドセーフになるのを妨げる要因をコンパイラーが見つけた場合にはコンパイルエラーが生成されます。:
注: スレッドセーフであるかどうかの検証は局所的に無効化することができます (後述の 部分的なスレッドセーフ検証の無効化 参照)
シンボルファイル(有効化されていた場合)には、それぞれのメソッドについてのスレッドセーフの状態が含まれます:
厳密に言えば"外部"アクセスにあたるため、フォームやデバッガなどのユーザーインターフェースオブジェクトへの呼び出しは、プリエンプティブスレッドでは許可されません。
プリエンプティブスレッドからアクセス可能なユーザーインターフェースは以下のものに限られます:
4Dコマンドのうち、大多数のものがスレッドセーフです。ドキュメントの中では、コマンドプロパティエリア内の のアイコンがコマンドがスレッドセーフであることを表しています。ランゲージリファレンスマニュアル内にてスレッドセーフであるコマンドの一覧を取得する事ができます。
また Command name を使用するとそれぞれのコマンドについてスレッドセーフかどうかのプロパティを取得する事ができます(以下参照)。
メソッドがトリガーを呼び出す事のできるコマンドを使用している場合、4Dコンパイラはメソッドがスレッドセーフであるかどうかをチェックするために、トリガーがスレッドセーフかどうかを評価します:
SAVE RECORD([Table_1]) //Table_1をトリガーし、存在すれば、スレッドセーフでなければならない
以下は、コンパイル時にトリガーがスレッドセーフであるかどうかをチェックされるコマンドの一覧です:
テーブルが動的に渡された場合、コンパイラはどのトリガーを評価すべきなのかが分からない場合があります。以下はそのような状況の一例です:
DEFAULT TABLE([Table_1])
SAVE RECORD
SAVE RECORD($ptrOnTable->)
SAVE RECORD(Table(myMethodThatReturnsATableNumber())->)
この場合、すべてのトリガーが評価されます。
少なくとも一つのトリガー内でスレッドセーフでないコマンドが検出された場合、グループ全体がチェックに失敗し、メソッドはスレッドアンセーフと宣言されます。
ON ERR CALLコマンドによって実装されたエラーキャッチメソッドは、プリエンプティブプロセスから呼び出される可能性が高いのであれば、スレッドセーフでなければなりません。このような状況を管理するため、コンパイラはコンパイル時にON ERR CALLコマンドに渡されたエラーキャッチプロジェクトメソッドのスレッドセーフプロパティをチェックするようになり、メソッドがプリエンプティブ実行に適応していない場合には適切なエラーを返します。
このチェックはメソッド名が定数として渡された場合にのみ可能であり、以下に示す様な、計算された値の場合にはチェックされないという点に注意して下さい:
ON ERR CALL("myErrMethod1") //コンパイラによってチェックされる
ON ERR CALL("myErrMethod"+String($vNum)) //コンパイラによるチェックはされない
これに加え、4D v15 R5以降、エラーキャッチプロジェクトメソッドがランタイムで呼び出す事ができない場合(スレッドセーフに関する問題がある、あるいは"メソッドが見つかりません"などの理由の場合)、新しいエラー-10532 "'methodName'というエラーハンドルメソッドを呼び出す事ができません"エラーが生成されます。
プロセスは、両プロセスがともにコオペラティブであった場合に限り、ポインターを参照して他のプロセス変数の値へアクセスすることができます。それ以外の場合、4Dはエラーを生成します。プリエンプティブプロセスにおいては、4Dコードがインタープロセス変数の値をポインター経由で参照しようとした場合、エラーが生成されます。
以下のメソッドでそのような例を考えます:
Method1:
myVar:=42
$pid:=New process("Method2";0;"process name";->myVar)
Method2:
$value:=$1->
Method1、あるいはMethod2を実行するプロセスのどちらか一つがプリエンプティブであった場合、"$value:=$1->"という式は実行エラーを生成します。
DocRef 参照についての詳細は DocRef: ドキュメント参照番号 を参照ください。
スレッドセーフではないが、実行されることもないと分かっているコマンドがコードに含まれている場合など、コードの一部をスレッドセーフ検証から除外したい場合があるかもしれません。
特定のコードを検証対象から除外するには、コメント形式の専用ディレクティブ %T- および %T+ でそのコードを挟みます。//%T- は以降のコードを検証から除外し、//%T+ は以降のコードに対する検証を有効に戻します:
// %T- 検証を無効にします
// スレッドセーフ検証から除外するコード
$w:=Open window(10;10;100;100) //例
// %T+ 検証を有効に戻します
無効化および有効化用のディレクティブでコードを挟んだ場合、そのコードがスレッドセーフかどうかについては、開発者が熟知している必要があります。プリエンプティブなスレッドでスレッドセーフでないコードが実行された場合には、ランタイムエラーが発生します。
プロダクト: 4D
テーマ: プロセス
初出: 4D v15 R5
ランゲージリファレンス ( 4D v19)
ランゲージリファレンス ( 4D v19.1)
ランゲージリファレンス ( 4D v19.4)
ランゲージリファレンス ( 4D v19.5)
ランゲージリファレンス ( 4D v19.6)
ランゲージリファレンス ( 4D v19.7)
ランゲージリファレンス ( 4D v19.8)