非同期プログラミング

ColdFusion では、Future を介した非同期プログラミングをサポートしています。Future は、非同期操作の最終的な結果です。

非同期プログラミングは、アプリケーションの平均応答時間を短縮したい場合に役立ちます。非同期プログラミングを使用して、IO やデータベースを集中的に使用するタスクをオフロードできます。また、UI の応答性を向上させる場合にも非同期プログラミングを使用します。

非同期プログラミングの利点の一部を以下に示します。

  • リアルタイムに近い処理
  • タスクの分散が容易
  • 独自のワーカースレッドの使用
  • 動的に設定可能なスレッドプール(Admin Console)
  • スレッドプールのオンデマンド作成

非同期プログラミングでは、runAsync 関数を使用できます。

給料が銀行口座に入金されたら、クレジットカード、住宅ローン、光熱費など、様々な請求書の支払いをおこないます。支払いは、給与がなければできません。つまり、この例では給与と連動しているということです。

<cfscript>
getAccountBalance = function(){
var balance = 120000;
return balance;
}
function payCreditCardBill(accountBalance){
var ccBill = 1890;
return accountBalance-ccBill;
}
payEMIs = function(accountBalance){
var mortgageEMI = 1000;
var carLeaseEMI = 750;
var healthInsuranceEMI = 250;
return accountBalance-(mortgageEMI+carLeaseEMI+healthInsuranceEMI);
}
miscellenousExpenses = function(accountBalance){
var shopping = 1500;
var clubExpense =1000;
var casinoExpense = 2000;
return accountBalance-(shopping+clubExpense+casinoExpense);
}
checkBalance = function(accountBalance){
while(accountBalance > 5000){
accountBalance = miscellenousExpenses(accountBalance);
}
if(accountBalance < 5000)
throw (message="Account balance below threshold!!!", type="info");
}
errorHandler = function(error){
if(error.message contains "Account balance below threshold!"){
return "You have reached your spending limit!";
}
}
future = runAsync(getAccountBalance).then(payCreditCardBill).then(payEMIs).
then(miscellenousExpenses).then(checkBalance).error(errorHandler);
writeOutput(future.get());
</cfscript>
<cfscript> getAccountBalance = function(){ var balance = 120000; return balance; } function payCreditCardBill(accountBalance){ var ccBill = 1890; return accountBalance-ccBill; } payEMIs = function(accountBalance){ var mortgageEMI = 1000; var carLeaseEMI = 750; var healthInsuranceEMI = 250; return accountBalance-(mortgageEMI+carLeaseEMI+healthInsuranceEMI); } miscellenousExpenses = function(accountBalance){ var shopping = 1500; var clubExpense =1000; var casinoExpense = 2000; return accountBalance-(shopping+clubExpense+casinoExpense); } checkBalance = function(accountBalance){ while(accountBalance > 5000){ accountBalance = miscellenousExpenses(accountBalance); } if(accountBalance < 5000) throw (message="Account balance below threshold!!!", type="info"); } errorHandler = function(error){ if(error.message contains "Account balance below threshold!"){ return "You have reached your spending limit!"; } } future = runAsync(getAccountBalance).then(payCreditCardBill).then(payEMIs). then(miscellenousExpenses).then(checkBalance).error(errorHandler); writeOutput(future.get()); </cfscript>
<cfscript>

 getAccountBalance = function(){
            var balance = 120000;
            return balance;
 }

 function payCreditCardBill(accountBalance){
            var ccBill = 1890;
            return accountBalance-ccBill;
 }

 payEMIs = function(accountBalance){
            var mortgageEMI = 1000;
            var carLeaseEMI = 750;
            var healthInsuranceEMI = 250;

            return accountBalance-(mortgageEMI+carLeaseEMI+healthInsuranceEMI);
 }

 miscellenousExpenses = function(accountBalance){
            var shopping = 1500;
            var clubExpense  =1000;
            var casinoExpense = 2000;
            return accountBalance-(shopping+clubExpense+casinoExpense);
 }

 checkBalance = function(accountBalance){
            while(accountBalance > 5000){
                        accountBalance = miscellenousExpenses(accountBalance);
                                    }
            if(accountBalance < 5000)
                        throw (message="Account balance below threshold!!!", type="info");
 }

 errorHandler = function(error){
            if(error.message contains "Account balance below threshold!"){
                        return "You have reached your spending limit!";
            }
 }

 future = runAsync(getAccountBalance).then(payCreditCardBill).then(payEMIs).
 then(miscellenousExpenses).then(checkBalance).error(errorHandler);

 writeOutput(future.get());

</cfscript>

また、クロージャと runAsync 関数を使用することもできます。

次に例を示します。

<cfscript>
future = runAsync(function(){return "I am invoked from RunAsync directly!";});
</cfscript>
<cfscript> future = runAsync(function(){return "I am invoked from RunAsync directly!";}); </cfscript>
<cfscript>
            future = runAsync(function(){return "I am invoked from RunAsync directly!";});
</cfscript>

runAsync で使用できるメソッドは以下のとおりです。

  • cancel();
  • error(callback, timeout);
  • error(callback);
  • get();
  • get(timeout
  • isCancelled();
  • isDone();
  • then(callback);
  • then(callback, timeout);

空の Future

空の Future はオブジェクトで、結果値を使用して明示的に完了とマークできます。producer-consumer シナリオで使用することができます。

次に例を示します。

<cfscript>
p = runAsync(); // empty future
p.complete(10);
writelog(p.get()); // displays 10
</cfscript>
<cfscript> p = runAsync(); // empty future p.complete(10); writelog(p.get()); // displays 10 </cfscript>
<cfscript>
 p = runAsync(); // empty future
 p.complete(10); 
 writelog(p.get()); // displays 10
</cfscript>

空の Future で使用できるメソッドは以下のとおりです。

  • cancel()
  • get()
  • isCancelled()
  • isDone()
  • complete(value)

Executor プールの設定

Administrator の「サーバーの設定」セクションには、「Executor プールの設定」オプションがあります。このオプションでは、次の値を指定できます。

  • コアプールサイズ:コアプールサイズは、保持しておくワーカースレッドの最小数です。この値は、「最大プールサイズ」に指定した値よりも小さくする必要があります。デフォルト値は 25 です。
  • 最大プールサイズ: プールで使用可能なスレッドの最大数です。デフォルト値は 50 です。
  • 保持時間: 作業を待っているアイドルスレッドのタイムアウトです(ミリ秒単位)。プールに corePoolSize を超えるスレッドが存在する場合にこのタイムアウトが使用されます。デフォルト値は 2,000 ミリ秒です。
Executor プールの設定
Executor Pool Configuration

これらの設定を使用すると、要件に応じて非同期 executor を細かく調整できます。また、これらのプロパティの変更は、サーバーを再起動しなくても反映されます。

さらに、上記のプロパティをサポートする次の Admin API も追加されています。これらの API は runtime.cfc に含まれています。

ColdFusion の 2018 リリースでは、プール設定をサポートするために、getRuntimeProperty(required propertyName) API に 3 つのプロパティが新しく追加されています。追加されたのは、次のプロパティです。

  • corePoolSize
  • maxPoolSize
  • keepAliveTime

次に例を示します。

<cfscript>
// Login is always required.
adminObj = createObject("component","cfide.adminapi.administrator");
adminObj.login("admin");
runtimeObj=createObject("component","cfide.adminapi.runtime");
corePool=runtimeObj.getRuntimeProperty("corePoolSize");
writeOutput("core pool size is: " & corePool & "<br/>");
maxPool=runtimeObj.getRuntimeProperty("maxPoolSize");
writeOutput("max pool size is: " & maxPool & "<br/>");
keepAlive=runtimeObj.getruntimeProperty("keepAliveTime");
writeOutput("keep alive time is: " & keepAlive & "<br/>");
</cfscript>
<cfscript> // Login is always required. adminObj = createObject("component","cfide.adminapi.administrator"); adminObj.login("admin"); runtimeObj=createObject("component","cfide.adminapi.runtime"); corePool=runtimeObj.getRuntimeProperty("corePoolSize"); writeOutput("core pool size is: " & corePool & "<br/>"); maxPool=runtimeObj.getRuntimeProperty("maxPoolSize"); writeOutput("max pool size is: " & maxPool & "<br/>"); keepAlive=runtimeObj.getruntimeProperty("keepAliveTime"); writeOutput("keep alive time is: " & keepAlive & "<br/>"); </cfscript>
<cfscript>
    // Login is always required.
    adminObj = createObject("component","cfide.adminapi.administrator");
    adminObj.login("admin");
    runtimeObj=createObject("component","cfide.adminapi.runtime");
    corePool=runtimeObj.getRuntimeProperty("corePoolSize");
    writeOutput("core pool size is: " & corePool & "<br/>");
    maxPool=runtimeObj.getRuntimeProperty("maxPoolSize");
    writeOutput("max pool size is: " & maxPool & "<br/>");
    keepAlive=runtimeObj.getruntimeProperty("keepAliveTime");
    writeOutput("keep alive time is: " & keepAlive & "<br/>");
</cfscript> 

ヘルプをすばやく簡単に入手

新規ユーザーの場合