Выполнение асинхронных операций в собственном клиенте SQL Server
Применимо: SQL Server База данных SQL Azure Управляемый экземпляр SQL Azure azure Synapse Analytics Analytics Platform System (PDW)
Внимание
SQL Server Native Client (SNAC) не поставляется с:
- SQL Server 2022 (16.x) и более поздних версий
- SQL Server Management Studio 19 и более поздних версий
Собственный клиент SQL Server (SQLNCLI или SQLNCLI11) и устаревший поставщик Microsoft OLE DB для SQL Server (SQLOLEDB) не рекомендуется для разработки новых приложений.
Для новых проектов используйте один из следующих драйверов:
Сведения о SQLNCLI, которые поставляется в качестве компонента SQL Server ядро СУБД (версии 2012–2019), см. в этом исключении жизненного цикла поддержки.
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);
}
См. также
Компоненты собственного клиента SQL Server
Свойства и поведение наборов строк
ISSAsynchStatus (OLE DB)