Поделиться через


Выполнение асинхронных операций

SQL Server позволяет приложениям выполнять асинхронные операции с базой данных. Асинхронная обработка позволяет методам передавать управление назад немедленно, не блокируя вызывающий поток. Это позволяет использовать значительную часть мощности и гибкости многопотоковой обработки, и разработчику при этом не требуется явно создавать потоки или обрабатывать синхронизацию. Приложения запрашивают асинхронную обработку при инициализации соединения с базой данных или при инициализации результата выполнения команды.

Открытие и закрытие соединения с базой данных

При использовании поставщика OLE DB для собственного клиента SQL Server приложения, способные асинхронно инициализировать объект источника данных, могут задать бит DBPROPVAL_ASYNCH_INITIALIZE в свойстве DBPROP_INIT_ASYNCH перед вызовом метода IDBInitialize::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 не возвращает значения, пока объект источника данных не будет инициализирован полностью.

ПримечаниеПримечание

Объекты источников данных, используемые для организации пула соединений, не могут вызывать интерфейс ISSAsynchStatus в поставщике OLE DB для собственного клиента SQL Server. Интерфейс ISSAsynchStatus недоступен для объединенных в пул объектов источников данных.

Если приложение явно и принудительно использует ядро курсора, IOpenRowset::OpenRowset и IMultipleResults::GetResult не будут поддерживать асинхронную обработку.

Кроме того, при удаленном взаимодействии библиотека DLL посредника-заглушки (в компонентах MDAC 2.8) не может вызвать интерфейс ISSAsynchStatus в собственном клиенте SQL Server. Интерфейс ISSAsynchStatus недоступен при удаленном взаимодействии.

Компоненты служб не поддерживают интерфейс ISSAsynchStatus.

Выполнение и инициализация наборов строк

Приложения, способные асинхронно открывать результаты выполнения команд, могут установить бит DBPROPVAL_ASYNCH_INITIALIZE в свойстве DBPROP_ROWSET_ASYNCH. Если задать этот бит перед вызовом IDBInitialize::Initialize, ICommand::Execute, IOpenRowset::OpenRowset или IMultipleResults::GetResult, значение аргумента riid должно быть IID_IDBAsynchStatus, IID_ISSAsynchStatus или IID_IUnknown.

Метод возвращается немедленно с результатом S_OK, если инициализация набора строк завершается немедленно, или DB_S_ASYNCHRONOUS, если инициализация набора строк продолжается асинхронно, а параметр ppRowset указывает запрошенный интерфейс для набора строк. Для поставщика OLE DB для собственного клиента SQL Server это может быть только интерфейс IDBAsynchStatus или ISSAsynchStatus. Пока набор строк не инициализирован полностью, этот интерфейс действует, как если бы он находился в режиме ожидания, и при вызове метода QueryInterface для получения интерфейсов, отличных от IID_IDBAsynchStatus или IID_ISSAsynchStatus, может быть возращена ошибка E_NOINTERFACE. Если потребитель явно не запросил асинхронную обработку, набор строк инициализируется синхронно. Все запрашиваемые интерфейсы доступны, когда интерфейс IDBAsynchStaus::GetStatus или ISSAsynchStatus::WaitForAsynchCompletion возвращается с указанием, что асинхронная операция завершена. Это не обязательно означает, что набор строк заполнен, но он завершен и полностью функционален.

Если выполненная команда не возвращает набор строк, она все равно возвращается немедленно с объектом, поддерживающим IDBAsynchStatus.

Если требуется получить несколько результатов асинхронного выполнения команды, выполните следующие действия.

  • Установите бит DBPROPVAL_ASYNCH_INITIALIZE свойства DBPROP_ROWSET_ASYNCH перед выполнением команды.

  • Вызовите метод ICommand::Execute и запросите интерфейс IMultipleResults.

Интерфейсы IDBAsynchStatus и ISSAsynchStatus могут быть получены путем запроса интерфейса множественных результатов с помощью метода QueryInterface.

Когда выполнение команды завершено, интерфейс IMultipleResults можно использовать обычным образом, с одним исключением от синхронной обработки: Может быть возвращен результат 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);
}