Share via


非同期操作の実行

SQL Server では、アプリケーションは非同期のデータベース操作を実行できます。非同期処理により、呼び出し側のスレッドをブロックしないで直ちに制御を返すことができるようになります。これは、マルチスレッドの持つ能力と柔軟性を大きく引き出し、開発者が明示的にスレッドを作成したり、同期を処理する手間を省くことができる機能です。アプリケーションは、データベース接続を初期化するときや、コマンドの実行結果を初期化するときに、非同期処理を要求します。

データベース接続の開閉

SQL Server Native Client OLE DB プロバイダを使用している場合、データ ソース オブジェクトを非同期に初期化するようにデザインされているアプリケーションは、IDBInitialize::Initialize を呼び出す前に DBPROP_INIT_ASYNCH プロパティの DBPROPVAL_ASYNCH_INITIALIZE ビットに設定できます。このプロパティが設定されると、プロバイダは Initialize の呼び出しからすぐに制御を戻し、操作が直ちに完了した場合は S_OK、初期化が非同期に続行される場合は DB_S_ASYNCHRONOUS を返します。アプリケーションは、データ ソース オブジェクトの IDBAsynchStatus または ISSAsynchStatusインターフェイスをクエリし、IDBAsynchStatus::GetStatusまたは ISSAsynchStatus::WaitForAsynchCompletion を呼び出して初期化の状態を取得できます。

また、DBPROPSET_SQLSERVERROWSET プロパティ セットに SSPROP_ISSAsynchStatus プロパティが追加されています。ISSAsynchStatus インターフェイスをサポートするプロバイダは、値 VARIANT_TRUE を指定してこのプロパティを実装する必要があります。

IDBAsynchStatus::Abort または ISSAsynchStatus::Abort を呼び出すと、非同期 Initialize 呼び出しを中止できます。コンシューマは、明示的にデータ ソースの非同期の初期化を要求できます。この要求を行わない場合、IDBInitialize::Initialize はデータ ソース オブジェクトが完全に初期化されるまで、制御を戻しません。

注意

接続プールに使われるデータ ソース オブジェクトは、SQL Server Native Client OLE DB プロバイダから ISSAsynchStatus インターフェイスを呼び出せません。ISSAsynchStatus インターフェイスは、プールされたデータ ソース オブジェクトには公開されません。

アプリケーションが明示的にカーソル エンジンの使用を設定している場合、IOpenRowset::OpenRowsetIMultipleResults::GetResult は非同期処理をサポートしません。

また、リモート処理プロキシ/スタブの dll (MDAC 2.8) は、SQL Server Native Client から ISSAsynchStatus インターフェイスを呼び出せません。ISSAsynchStatus インターフェイスは、リモート処理経由では公開されません。

サービス コンポーネントは、ISSAsynchStatus をサポートしません。

実行と行セットの初期化

コマンドの実行結果を非同期に開くようデザインされているアプリケーションは、DBPROP_ROWSET_ASYNCH プロパティに DBPROPVAL_ASYNCH_INITIALIZE ビットを設定できます。IDBInitialize::InitializeICommand::ExecuteIOpenRowset::OpenRowset または IMultipleResults::GetResult を呼び出す前にこのビットを設定するときは、riid 引数を IID_IDBAsynchStatus、IID_ISSAsynchStatus、または IID_IUnknown に設定する必要があります。

メソッドはすぐに制御を戻し、行セットの初期化が直ちに完了した場合は S_OK、行セットの初期化が非同期に続行される場合は DB_S_ASYNCHRONOUS を返して、ppRowset を行セット上の要求されたインターフェイスに設定します。SQL Server Native Client OLE DB プロバイダの場合、このインターフェイスは IDBAsynchStatus または ISSAsynchStatus のいずれかです。このインターフェイスは、行セットが完全に初期化されるまでは中断状態にあるかのように動作し、IID_IDBAsynchStatus または IID_ISSAsynchStatus 以外のインターフェイスに対して QueryInterface が呼び出された場合、E_NOINTERFACE を返すことがあります。コンシューマが明示的に非同期処理を要求しない限り、行セットは同期的に初期化されます。IDBAsynchStaus::GetStatus または ISSAsynchStatus::WaitForAsynchCompletion が非同期操作が完了したことを示す値を返した場合、要求したすべてのインターフェイスを使用できます。これは、必ずしも行セットに完全にデータが格納されたことを意味するものではありませんが、行セットは完成し、完全に機能します。

実行されたコマンドが行セットを返さない場合でも、このコマンドは、IDBAsynchStatus をサポートするオブジェクトを直ちに返します。

非同期コマンドの実行により複数の結果を取得する必要がある場合は、次の操作を行います。

  • コマンドを実行する前に、DBPROP_ROWSET_ASYNCH プロパティに DBPROPVAL_ASYNCH_INITIALIZE ビットを設定します。

  • ICommand::Execute を呼び出し、IMultipleResults を要求します。

その結果、QueryInterface を使用して複数の結果のインターフェイスをクエリすることで、IDBAsynchStatus および ISSAsynchStatus インターフェイスを取得できるようになります。

コマンドの実行が完了すると、IMultipleResults を通常どおり使用できます。ただし、非同期処理の場合は 1 つだけ例外があり、DB_S_ASYNCHRONOUS が返される可能性があります。この場合、IDBAsynchStatus または ISSAsynchStatus を使用して、操作が完了しているかどうかを確認できます。

次の例では、アプリケーションが非ブロッキング メソッドを呼び出し、いくつか他の処理を実行し、制御を戻して結果を処理します。ISSAsynchStatus::WaitForAsynchCompletion は、非同期実行操作が完了するか、dwMilisecTimeOut により指定された時間が経過するまで、内部イベント オブジェクト上で待機します。

// Set the DBPROPVAL_ASYNCH_INITIALIZE bit in the 
// DBPROP_ROWSET_ASYNCH property before calling Execute().

DBPROPSET CmdPropset[1];
DBPROP CmdProperties[1];

CmdPropset[0].rgProperties = CmdProperties;
CmdPropset[0].cProperties = 1;
CmdPropset[0].guidPropertySet = DBPROPSET_ROWSET;

// Set asynch mode for command.
CmdProperties[0].dwPropertyID = DBPROP_ROWSET_ASYNCH;
CmdProperties[0].vValue.vt = VT_I4;
CmdProperties[0].vValue.lVal = DBPROPVAL_ASYNCH_INITIALIZE;
CmdProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;

hr = pICommandProps->SetProperties(1, CmdPropset);

hr = pICommand->Execute(
   pUnkOuter,
   IID_ISSAsynchStatus,
   pParams,
   pcRowsAffected,
   (IUnknown**)&pISSAsynchStatus);

if (hr == DB_S_ASYNCHRONOUS)
{
   // Do some work here...

   hr = pISSAsynchStatus->WaitForAsynchCompletion(dwMilisecTimeOut);
   if ( hr == S_OK)
   {
      hr = pISSAsynchStatus->QueryInterface(IID_IRowset, (void**)&pRowset);
      pISSAsynchStatus->Release();
   }
}

ISSAsynchStatus::WaitForAsynchCompletion は、非同期実行操作が完了するか、dwMilisecTimeOut の値が示す時間が経過するまで、内部イベント オブジェクト上で待機します。

次の例は、複数の結果セットを返す非同期処理のコードです。

DBPROP CmdProperties[1];

// Set asynch mode for command.
CmdProperties[0].dwPropertyID = DBPROP_ROWSET_ASYNCH;
CmdProperties[0].vValue.vt = VT_I4;
CmdProperties[0].vValue.lVal = DBPROPVAL_ASYNCH_INITIALIZE;

hr = pICommand->Execute(
   pUnkOuter,
   IID_IMultipleResults,
   pParams,
   pcRowsAffected,
   (IUnknown**)&pIMultipleResults);

// Use GetResults for ISSAsynchStatus.
hr = pIMultipleResults->GetResult(IID_ISSAsynchStatus, (void **) &pISSAsynchStatus);

if (hr == DB_S_ASYNCHRONOUS)
{
   // Do some work here...

   hr = pISSAsynchStatus->WaitForAsynchCompletion(dwMilisecTimeOut);
   if (hr == S_OK)
   {
      hr = pISSAsynchStatus->QueryInterface(IID_IRowset, (void**)&pRowset);
      pISSAsynchStatus->Release();
   }
}

ブロックを防ぐため、次の例のように、クライアントでは実行中の非同期操作の状態を確認できます。

// Set the DBPROPVAL_ASYNCH_INITIALIZE bit in the 
// DBPROP_ROWSET_ASYNCH property before calling Execute().
hr = pICommand->Execute(
   pUnkOuter,
   IID_ISSAsynchStatus,
   pParams,
   pcRowsAffected,
   (IUnknown**)&pISSAsynchStatus); 

if (hr == DB_S_ASYNCHRONOUS)
{
   do{
      // Do some work...
      hr = pISSAsynchStatus->GetStatus(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN, NULL, NULL, &ulAsynchPhase, NULL);
   }while (DBASYNCHPHASE_COMPLETE != ulAsynchPhase)
   if SUCCEEDED(hr)
   {
      hr = pISSAsynchStatus->QueryInterface(IID_IRowset, (void**)&pRowset);
   }
   pIDBAsynchStatus->Release();
}

次の例は、現在実行中の非同期操作を中止する方法を示しています。

// Set the DBPROPVAL_ASYNCH_INITIALIZE bit in the 
// DBPROP_ROWSET_ASYNCH property before calling Execute().
hr = pICommand->Execute(
   pUnkOuter,
   IID_ISSAsynchStatus,
   pParams,
   pcRowsAffected,
   (IUnknown**)&pISSAsynchStatus);

if (hr == DB_S_ASYNCHRONOUS)
{
   // Do some work...
   hr = pISSAsynchStatus->Abort(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN);
}