执行异步操作

SQL Server 允许应用程序执行异步数据库操作。异步处理将使方法能够立即返回,而不会阻塞调用线程。这使得多线程机制能提供更强大的功能和灵活性,而不需要开发人员显式创建线程或处理同步。应用程序将在初始化数据库连接时或初始化由执行命令所生成的结果时请求异步处理。

打开和关闭数据库连接

使用 SQL Server Native Client OLE DB 访问接口时,被设计为以异步方式初始化数据源对象的应用程序可以在调用 IDBInitialize::Initialize 之前,设置 DBPROP_INIT_ASYNCH 属性中的 DBPROPVAL_ASYNCH_INITIALIZE 位。设置该属性后,访问接口将从对 Initialize 的调用立即返回 S_OK(如果操作已经立即完成)或 DB_S_ASYNCHRONOUS(如果初始化正在以异步方式继续进行)。应用程序可以查询数据源对象的 IDBAsynchStatusISSAsynchStatus 接口,然后调用 IDBAsynchStatus::GetStatusISSAsynchStatus::WaitForAsynchCompletion 以获得初始化的状态。

此外,SSPROP_ISSAsynchStatus 属性已添加到 DBPROPSET_SQLSERVERROWSET 属性集。支持 ISSAsynchStatus 接口的访问接口必须以值 VARIANT_TRUE 来实现该属性。

可以调用 IDBAsynchStatus::AbortISSAsynchStatus::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::OpenRowsetIMultipleResults::GetResult 之前设置该位时,必须将 riid 参数设置为 IID_IDBAsynchStatus、IID_ISSAsynchStatus 或 IID_IUnknown。

该方法将立即返回 S_OK(如果行集初始化立即完成)或 DB_S_ASYNCHRONOUS(如果行集继续执行异步初始化),并且 ppRowset 将设置为行集上的请求接口。对于 SQL Server Native Client OLE DB 访问接口,该接口只能是 IDBAsynchStatusISSAsynchStatus。在行集完全初始化之前,该接口的行为就好象它处于挂起状态中,并且对除 IID_IDBAsynchStatusIID_ISSAsynchStatus 以外的接口调用 QueryInterface 可能返回 E_NOINTERFACE。除非使用者显式请求异步处理,否则行集将以同步方式执行初始化。如果 IDBAsynchStaus::GetStatusISSAsynchStatus::WaitForAsynchCompletion 返回的内容指示异步操作已完成,则所有被请求的接口均可用。这不一定意味着行集已完全填充,但它是完整的,且功能齐全。

如果所执行的命令未返回行集,则它仍然会立即返回支持 IDBAsynchStatus 的对象。

如果需要获得由异步命令执行所产生的多个结果,则应当:

  • 在执行命令之前,设置 DBPROP_ROWSET_ASYNCH 属性的 DBPROPVAL_ASYNCH_INITIALIZE 位。

  • 调用 ICommand::Execute,并请求 IMultipleResults

然后可以通过使用 QueryInterface 来查询多个结果接口,以获得 IDBAsynchStatusISSAsynchStatus 接口。

命令执行完成时,可以按正常方式使用 IMultipleResults,但对于同步情况存在一个例外:可能返回 DB_S_ASYNCHRONOUS,这时可以用 IDBAsynchStatusISSAsynchStatus 确定操作是何时完成的。

示例

在以下示例中,应用程序调用非阻止方法,执行一些其他处理,然后返回以处理结果。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);
}