4D v16.3

プリエンプティブ4Dプロセス

ホーム

 
4D v16.3
プリエンプティブ4Dプロセス

プリエンプティブ4Dプロセス  


 

 

OS X用 4D Developer Edition 64-bit版(Windows用は4D v16 R2以降利用可能)は プリエンプティブ 4D コード の扱いを可能にする、強力な機能を提供しています。この新機能のおかげで、コンパイルされた 4Dアプリケーションはマルチコアコンピューターの利点をすべて活かすことができ、それによって実行速度が向上し、またより多くのユーザーの接続をサポートすることができます。

プリエンプティブモードで実行された場合、プロセスはCPUに割り当てられます。プロセス管理はシステムへと委任され、マルチコアのマシン上にてシステムはプロセスをそれぞれのCPUへと個別に割り当てます。

コオペラティブモード (4D v15 R5 までは、こちらのみ使用可能) で実行された場合には、たとえマルチコアのマシン上であっても、すべてのプロセスは親アプリケーションのスレッドにより管理され、同じCPUを共有します。

結果として、プリエンプティブモードでは、アプリケーションの全体的なパフォーマンスは向上します。マルチコアのマシン上では複数のプロセス (スレッド) が真実同時実行可能であるため、パフォーマンスの向上はさらに顕著になります。

その一方で、プリエンプティブモードではそれぞれのスレッドは他から独立しており、アプリケーションによって直接管理されている訳ではないため、プリエンプティブに準拠させたいメソッドには特定の制約が課されます。それに加え、プリエンプティブ実行は特定のコンテキストでのみ使用可能です。

プリエンプティブモードの使用は、4D 64-bit版においてのみ可能です。

以下の実行コンテキストが現在サポートされています:

プリエンプティブ実行
4D Server
4D リモート-
4D シングルユーザー
コンパイル済みモード
インタープリターモード-

実行コンテキストがプリエンプティブモードをサポートし、かつメソッドが "スレッドセーフ" である場合、New process あるいは CALL WORKERコマンドあるいは "メソッドを実行" メニュー項目を使用してローンチされた新しい 4Dプロセスは、プリエンプティブスレッド内にて実行されます。

それ以外の場合で、サポートされていない実行コンテキスト (例えばリモート 4Dマシンなど) から New process あるいは CALL WORKER コマンドを呼び出した場合、プロセスは常にコオペラティブに実行されます。

注: 例えば Execute on server などのランゲージを使用したストアドプロシージャーをサーバー上で開始する事で、4Dリモートからでもプロセスをプリエンプティブに実行する事が可能です。

4Dコードは、いくつかの特定の条件に合致していた場合に限りプリエンプティブスレッド内で実行することができます。実行コードのそれぞれの部分 (コマンド、メソッド、変数など) がプリエンプティブ実行に準拠している必要性があります。プリエンプティブスレッドで実行可能な要素はスレッドセーフと呼ばれ、プリエンプティブスレッドで実行できない要素はスレッドアンセーフと呼ばれます。

注: スレッドは親プロセスメソッドをスタートとして独自に管理されているので、呼び出しチェーン全体のどこにおいてもスレッドアンセーフなコードが含まれていてはいけません。そのようなコードが含まれていた場合、プリエンプティブに実行することはできません。この点については、プロセスがプリエンプティブに実行される条件とは? の章で詳細な説明があります。

それぞれの "スレッドセーフティ" プロパティは、要素自身によります:

  • 4Dコマンド: スレッドセーフティは内部プロパティです。ランゲージリファレンス内では、スレッドセーフなコマンドは のアイコンで識別されています。4Dコマンドの大部分はプリエンプティブモードで実行可能です。
  • プロジェクトメソッド: スレッドセーフであるための条件は スレッドセーフなメソッドの書き方 の段落にまとめられています。

原則として、プリエンプティブスレッド内で実行されるコードは外部との相互作用する部分、例えばプラグインコードやインタープロセス変数などを呼び出すことはできません。しかしながら 4Dデータサーバーはプリエンプティ実行をサポートしていることから、データアクセスは可能です。

デフォルトでは、4Dはすべてのプロジェクトメソッドをコオペラティブモードで実行します。プリエンプティブモードを利用したい場合は、まず最初に、可能な限りプリエンプティブモードで開始したいメソッドをすべて明示的に宣言することから始まります。これはつまり、プリエンプティブプロセスで実行可能なメソッドであるということです。コンパイラーは、それらのメソッドが実際にスレッドセーフであるかどうかをチェックします (詳細な情報については スレッドセーフなメソッドの書き方 を参照してください)。また、必要であれば、一部のメソッドに対してプリエンプティブモードを禁止することもできます。

プリエンプティブで使用可能 ("capable") であると宣言することは、当該メソッドにプリエンプティブ実行の資格を与えますが、実行時にそのメソッドが実際にプリエンプティブモードで実行されることを保障するものではないことに留意が必要です。プロセスをプリエンプティブモードで開始することは、プロセス内の呼び出しチェーン内のすべてのメソッドの、関連プロパティを4Dが評価して初めて可能になります (より詳細な情報については、プロセスがプリエンプティブに実行される条件とは? の段落を参照してください)。

メソッドがプリエンプティブモードに則していることを宣言するためには、メソッドプロパティダイアログボックスの実行モード宣言オプションを使用する必要があります:

 

以下のオプションが提供されています:

  • プリエンプティブプロセスで実行可能: このオプションをチェックすると、メソッドがプリエンプティブプロセスでの実行が可能であると宣言し、可能な場合にはプリエンプティブモードで実行するべきと宣言します。メソッドの "プリエンプティブ" プロパティは "capable" に設定されます。
    このオプションがチェックされていた場合、4Dコンパイラーはメソッドが実際にプリエンプティブモードで実行可能かどうかを検証し、そうでない場合 (例えば、プリエンプティブモードで実行不可能なコマンドやメソッドを直接的あるいは間接的に呼び出している場合など) にはエラーを返します。その場合にはメソッドを編集してスレッドセーフにするか、あるいは別のオプションを選択します。
    メソッドのプリエンプティブ性が証明されると、内部で "thread safe" というタグ付けがされ、すべての要件が満たされればプリエンプティブモードで実行されます。このプロパティはプリエンプティブモードの資格を定義しますが、メソッドが実際にプリエンプティブモードで実行されることを保証するものではありません。実行モードは特定のコンテキストを必要とするからです ( を参照して下さい)。
  • プリエンプティブプロセスでは実行不可: このオプションをチェックすると、メソッドがプリエンプティブモードで実行されてはならないと宣言し、以前の 4Dのバージョンと同じように常にコオペラティブモードで実行されます。"プリエンプティブ" プロパティは "incapable" に設定されます。
    このオプションがチェックされている場合、4Dコンパイラーはメソッドがプリエンプティブに実行可能かどうかを検証しません。メソッドは内部で自動的に "thread unsafe" とタグ付けされます (例え理論的にはスレッドセーフであってもです)。ランタイムに呼び出された場合、このメソッドは同じスレッド内の他のメソッドを "汚染" し、例え他のメソッドがスレッドセーフであったとしても、スレッド実行はコオペラティブモードを強制されます。
  • 特に設定しない (デフォルト): このオプションをチェックすると、メソッドのプリエンプティブプロパティを管理したくないということを宣言します。メソッドの "プリエンプティブ" プロパティは "indifferent" に設定されます。
    このオプションがチェックされているとき、4Dコンパイラーはメソッドのプリエンプティブな実効性を評価し、内部的に "thread safe" あるいは "thread unsafe" のタグ付けをします。プリエンプティブ実行に関するエラーは報告されません。メソッドがスレッドセーフと評価されていれば、実行時にプリエンプティブコンテキストから呼び出された場合にはプリエンプティブスレッド実行を禁止しません。逆に、メソッドがスレッドアンセーフであると評価された場合には、呼び出されたときにどのようなプリエンプティブスレッド実行も許可しません。
    このオプションを使用した場合、内部でのスレッドセーフの評価に関わらず、メソッドが 4Dから最初の親メソッドとして直接呼び出された場合 (例えばコマンドから呼び出された場合など)、メソッドは必ずコオペラティブモードで実行されます。内部で "スレッドセーフ" とタグ付けされていた場合には、そのタグは呼び出しチェーン内の他のメソッドから呼び出された場合に限り考慮されます。
 

注: "コンポーネントとホスト間で共有" と宣言されたコンポーネントメソッドも、ホストデータベースによってプリエンプティブプロセスで実行される場合には "capable" と宣言される必要があります。

例えば METHOD GET CODE を使用して、メソッドのコードを書き出す場合、"プリエンプティブ" プロパティは "capable"、あるいは "incapable" の値で "%attributes" コメント内に書き出されます ("indifferent" オプションを選択している場合には、このプロパティは提供されません)。METHOD GET ATTRIBUTES および METHOD SET ATTRIBUTES コマンドも、"プリエンプティブ" 属性の値 ("capable", "incapable", "indifferent") を取得、あるいは指定します。

以下の表はプリエンプティブモードの宣言オプションをまとめたものです:

オプションプリエンプティブプロパティ値 (インタープリター)コンパイラーの挙動内部タグ (コンパイル済み)呼び出しチェーンがスレッドセーフだった際の実行モード
プリエンプティブプロセス内で実行可能capable資格をチェックし、不可能だった場合にはエラーを返しますスレッドセーフプリエンプティブ
プリエンプティブプロセス内で実行不可能incapable評価しませんスレッドアンセーフコオペラティブ
無関係indifferent評価しますが、エラーを返しませんスレッドセーフあるいはスレッドアンセーフ スレッドセーフの場合: プリエンプティブ; スレッドアンセーフの場合: コオペラティブ; 直接呼びされた場合: コオペラティブ

リマインダー: プリエンプティブ実行はコンパイル済みモードでのみ利用可能です。

コンパイル済みモードでは、New process あるいは CALL WORKER メソッドで作成されたプロセスを開始するとき、4Dはプロセスメソッド (別名親メソッド) のプリエンプティブプロパティを読み、そのプロパティに応じてプロセスをプリエンプティブモードあるいはコオペラティブモードで実行します:

  • プロセスメソッドが "thread safe" であった場合 (コンパイル時に評価)、プロセスはプリエンプティブスレッド内で実行されます。
  • プロセスメソッドが "thread unsafe" であった場合、プロセスはコオペラティブスレッド内で実行されます。
  • プロセスメソッドのプリエンプティブプロパティが "indifferent" であった場合、(メソッドが実際にはプリエンプティブに実行可能だったとしても) 互換性のためにプロセスはコオペラティブスレッド内で実行されます。この互換性機能はメソッドがプロセスメソッドとして使用された場合にのみ適用されるという点に注意してください。また "indifferent" と宣言されたもののコンパイラーによって内部で "thread safe" とタグ付けされたメソッドに関しては、他のメソッドからプリエンプティブに呼び出すことが可能です (以下参照)。

実際のスレッドセーフプロパティは呼び出しチェーンによります。"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ではプロセスに対してコオペラティブ実行かプリエンプティブ実行かを識別する新機能を提供しています:

  • PROCESS PROPERTIESコマンドを使用するとプロセスがプリエンプティブモードあるいはコオペラティブモードで実行されているかを調べる事ができます。
  • ランタイムエクスプローラーと4D Server管理ウィンドウは、どちらもプリエンプティブプロセス(と新しいワーカープロセス)に対して新しい特定のアイコンを表示するようになりました:
    プロセス型アイコン
    プリエンプティブストアドプロシージャ
    プリエンプティブワーカープロセス
    コオペラティブワーカープロセス

    注:
    ランタイムエクスプローラーインターフェースに加え、他の既存のプロセスアイコンは4Dおよび4D Server v15 R5においてアップデートされました。

スレッドセーフであるためには、メソッドは以下のルールに従う必要があります:

  • "プリエンプティブプロセスで実行可能"もしくは"無関係"プロパティを持っている
  • スレッドセーフでない4Dコマンドを呼び出していない
  • スレッドセーフでない他のプロジェクトメソッドを呼び出していない
  • プラグインを呼び出していない
  • Begin SQL/End SQL コードブロックを使用していない
  • インタープロセス変数を使用していない(*)
  • インターフェースオブジェクトを呼び出していない(**) (例外あり、以下参照)

注: "コンポーネントとホストデータベース間で共有"メソッドの場合、"プリエンプティブプロセスで実行可能"プロパティが選択されている必要があります。

(*) ワーカープロセスという新種のプロセスによって、プリエンプティブプロセスを含むあらゆるプロセス間ででーたの交換ができるようになります。より詳細な情報に着いてはプロセス間のメッセージ通信を参照して下さい。
(**) 新しいCALL FORM コマンドは、プリエンプティブプロセスからインターフェースオブジェクトを呼び出すためのエレガントな(???)ソリューションを提供します。

"プリエンプティブプロセスで実行可能"プロパティを持つメソッドは、コンパイル時に4Dによってチェックされます。メソッドがスレッドセーフになるのを妨げる要因をコンパイラーが見つけた場合にはコンパイルエラーが生成されます。:

シンボルファイル(有効化されていた場合)には、それぞれのメソッドについてのスレッドセーフの状態が含まれます:

厳密に言えば"外部"アクセスにあたるため、フォームやデバッガなどのユーザーインターフェースオブジェクトへの呼び出しは、プリエンプティブスレッドでは許可されません。

プリエンプティブスレッドからアクセス可能なユーザーインターフェースは以下のものに限られます:

  • 標準のエラーダイアログ。ダイアログはユーザーモードプロセス(4Dシングルユーザー)、あるいはサーバーユーザーインターフェースプロセス(4D Server)内で表示されます。ただしトレースボタンは無効化されます。
  • 標準の進捗バー。
  • ALERT、REQUESTそしてCONFIRMダイアログ。ダイアログはユーザーモードプロセス(4Dシングルユーザー)、あるいはサーバーユーザーインターフェースプロセス(4D Server)内で表示されます。
    ただし4D ServerをWindows上でユーザー操作を許可しないサービスとしてローンチした場合には、ダイアログは表示されないという点に注意して下さい。

4Dコマンドのうち、大多数のものがスレッドセーフです。ドキュメントの中では、コマンドプロパティエリア内の のアイコンがコマンドがスレッドセーフであることを表しています。ランゲージリファレンスマニュアル内にてスレッドセーフであるコマンドの一覧を取得する事ができます。

また Command name を使用するとそれぞれのコマンドについてスレッドセーフかどうかのプロパティを取得する事ができます(以下参照)。

メソッドがトリガーを呼び出す事のできるコマンドを使用している場合、4Dコンパイラはメソッドがスレッドセーフであるかどうかをチェックするために、トリガーがスレッドセーフかどうかを評価します:

 SAVE RECORD([Table_1]//Table_1をトリガーし、存在すれば、スレッドセーフでなければならない

以下は、コンパイル時にトリガーがスレッドセーフであるかどうかをチェックされるコマンドの一覧です:

  • SAVE RECORD
  • SAVE RELATED ONE
  • DELETE RECORD
  • DELETE SELECTION
  • ARRAY TO SELECTION
  • JSON TO SELECTION
  • APPLY TO SELECTION
  • IMPORT DATA
  • IMPORT DIF
  • IMPORT ODBC
  • IMPORT SYLK
  • IMPORT TEXT

テーブルが動的に渡された場合、コンパイラはどのトリガーを評価すべきなのかが分からない場合があります。以下はそのような状況の一例です:

 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 参照番号 (開かれたドキュメントの参照番号で次のコマンドに使用、または戻り値として返されます: Open document, Create document, Append document, CLOSE DOCUMENT, RECEIVE PACKET, SEND PACKET) の使用は次のコンテキストに限られます:
  • プリエンプティブプロセスからコールされた場合に生成される DocRef 参照は同プリエンプティブプロセスでのみ使用可能です。
  • コオペラティブプロセスからコールされた場合に生成される DocRef 参照は別のコオペラティブプロセスでも使用可能です。

DocRef 参照についての詳細は DocRef: ドキュメント参照番号 を参照ください。



参照 

Command name
プリエンプティブWebプロセスの使用

 
プロパティ 

プロダクト: 4D
テーマ: プロセス

 
履歴 

初出: 4D v15 R5

 
ARTICLE USAGE

ランゲージリファレンス ( 4D v16.1)
ランゲージリファレンス ( 4D v16.2)
ランゲージリファレンス ( 4D v16.3)