Асинхронное выполнение (метод уведомления)
ODBC позволяет асинхронно выполнять операции подключения и инструкции. Поток приложения может вызывать функцию ODBC в асинхронном режиме, и функция может вернуться до завершения операции, позволяя потоку приложения выполнять другие задачи. В пакете SDK для Windows 7 для асинхронных инструкций или операций подключения приложение определило, что асинхронная операция была завершена с помощью метода опроса. Дополнительные сведения см. в разделе "Асинхронное выполнение" (метод опроса). Начиная с пакета SDK для Windows 8, можно определить, что асинхронная операция завершена с помощью метода уведомления.
В методе опроса приложения должны вызывать асинхронную функцию каждый раз, когда она хочет состояние операции. Метод уведомления аналогичен обратному вызову и ожиданию в ADO.NET. Однако ODBC использует события Win32 в качестве объекта уведомления.
Одновременно нельзя использовать библиотеку курсоров ODBC и асинхронное уведомление ODBC. Установка обоих атрибутов возвращает ошибку с SQLSTATE S1119 (библиотека курсоров и асинхронное уведомление нельзя включить одновременно).
Сведения о завершении асинхронной функции см. в уведомлении о завершении асинхронной функции для разработчиков драйверов.
Примечание.
Метод уведомления не поддерживается библиотекой курсоров. Приложение получит сообщение об ошибке, если он пытается включить библиотеку курсоров через SQLSet Подключение Attr, когда метод уведомления включен.
Обзор
При вызове функции ODBC в асинхронном режиме элемент управления возвращается вызывающей приложению немедленно с помощью SQL_STILL_EXECUTING возвращаемого кода. Приложение должно повторно опрашивить функцию, пока она не возвращает что-либо, отличное от SQL_STILL_EXECUTING. Цикл опроса увеличивает загрузку ЦП, что приводит к снижению производительности во многих асинхронных сценариях.
При использовании модели уведомлений модель опроса отключается. Приложения не должны снова вызывать исходную функцию. Вызовите функцию SQLCompleteAsync, чтобы завершить асинхронную операцию. Если приложение снова вызывает исходную функцию до завершения асинхронной операции, вызов вернет SQL_ERROR с SQLSTATE IM017 (опрос отключен в режиме асинхронного уведомления).
При использовании модели уведомлений приложение может вызвать SQLCancel или SQLCancelHandle, чтобы отменить инструкцию или операцию подключения. Если запрос отмены выполнен успешно, ODBC вернет SQL_SUCCESS. Это сообщение не указывает, что функция была фактически отменена; указывает, что запрос на отмену был обработан. Независимо от того, зависит ли функция от драйвера и зависит от источника данных. При отмене операции диспетчер драйверов по-прежнему сигнализирует о событии. Диспетчер драйверов возвращает SQL_ERROR в буфере возвращаемого кода, а состояние — SQLSTATE HY008 (операция отменена), чтобы указать, что отмена выполнена успешно. Если функция завершила обычную обработку, диспетчер драйверов возвращает SQL_SUCCESS или SQL_SUCCESS_WITH_INFO.
Поведение нижнего уровня
Версия диспетчера драйверов ODBC, поддерживающая это уведомление, — ODBC 3.81.
Версия ODBC приложения | Версия Диспетчера драйверов | Версия драйвера | Поведение |
---|---|---|---|
Новое приложение любой версии ODBC | ODBC 3.81 | Драйвер ODBC 3.80 | Приложение может использовать эту функцию, если драйвер поддерживает эту функцию, в противном случае диспетчер драйверов завершит ошибку. |
Новое приложение любой версии ODBC | ODBC 3.81 | Драйвер pre-ODBC 3.80 | Диспетчер драйверов ошибится, если драйвер не поддерживает эту функцию. |
Новое приложение любой версии ODBC | Предварительная версии ODBC 3.81 | Любое | Когда приложение использует эту функцию, старый диспетчер драйверов будет рассматривать новые атрибуты как атрибуты, относящиеся к драйверу, и драйвер должен ошибиться. Новый диспетчер драйверов не передает эти атрибуты драйверу. |
Приложение должно проверка версию Диспетчера драйверов перед использованием этой функции. В противном случае, если плохо написанный драйвер не ошибается, а версия диспетчера драйверов пред ODBC 3.81 не определена.
Варианты использования
В этом разделе показаны варианты использования для асинхронного выполнения и механизма опроса.
Интеграция данных из нескольких источников ODBC
Приложение интеграции с данными асинхронно извлекает данные из нескольких источников данных. Некоторые данные из удаленных источников данных, а некоторые — из локальных файлов. Приложение не может продолжаться до завершения асинхронных операций.
Вместо повторного опроса операции, чтобы определить, завершена ли операция, приложение может создать объект события и связать его с дескриптором подключения ODBC или дескриптором инструкции ODBC. Затем приложение вызывает API синхронизации операционной системы, чтобы ждать одного объекта события или многих объектов событий (как события ODBC, так и другие события Windows). ODBC сигнализирует объекту события при завершении соответствующей асинхронной операции ODBC.
В Windows будут использоваться объекты событий Win32, которые будут предоставлять пользователю единую модель программирования. Диспетчеры драйверов на других платформах могут использовать реализацию объекта событий, относящиеся к этим платформам.
В следующем примере кода показано использование асинхронного уведомления подключения и инструкции:
// This function opens NUMBER_OPERATIONS connections and executes one query on statement of each connection.
// Asynchronous Notification is used
#define NUMBER_OPERATIONS 5
int AsyncNotificationSample(void)
{
RETCODE rc;
SQLHENV hEnv = NULL;
SQLHDBC arhDbc[NUMBER_OPERATIONS] = {NULL};
SQLHSTMT arhStmt[NUMBER_OPERATIONS] = {NULL};
HANDLE arhDBCEvent[NUMBER_OPERATIONS] = {NULL};
RETCODE arrcDBC[NUMBER_OPERATIONS] = {0};
HANDLE arhSTMTEvent[NUMBER_OPERATIONS] = {NULL};
RETCODE arrcSTMT[NUMBER_OPERATIONS] = {0};
rc = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &hEnv);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
rc = SQLSetEnvAttr(hEnv,
SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3_80,
SQL_IS_INTEGER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
// Connection operations begin here
// Alloc NUMBER_OPERATIONS connection handles
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &arhDbc[i]);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Enable DBC Async on all connection handles
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc= SQLSetConnectAttr(arhDbc[i], SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, (SQLPOINTER)SQL_ASYNC_DBC_ENABLE_ON, SQL_IS_INTEGER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Application must create event objects
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
arhDBCEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto-reset, initial state is not-signaled
if (!arhDBCEvent[i]) goto Cleanup;
}
// Enable notification on all connection handles
// Event
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc= SQLSetConnectAttr(arhDbc[i], SQL_ATTR_ASYNC_DBC_EVENT, arhDBCEvent[i], SQL_IS_POINTER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Initiate connect establishing
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLDriverConnect(arhDbc[i], NULL, (SQLTCHAR*)TEXT("Driver={ODBC Driver 11 for SQL Server};SERVER=dp-srv-sql2k;DATABASE=pubs;UID=sa;PWD=XYZ;"), SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
}
// Can do some other staff before calling WaitForMultipleObjects
WaitForMultipleObjects(NUMBER_OPERATIONS, arhDBCEvent, TRUE, INFINITE); // Wait All
// Complete connect API calls
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLCompleteAsync(SQL_HANDLE_DBC, arhDbc[i], & arrcDBC[i]);
}
BOOL fFail = FALSE; // Whether some connection openning fails.
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if ( !SQL_SUCCEEDED(arrcDBC[i]) )
fFail = TRUE;
}
// If some SQLDriverConnect() fail, clean up.
if (fFail)
{
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (SQL_SUCCEEDED(arrcDBC[i]) )
{
SQLDisconnect(arhDbc[i]); // This is also async
}
else
{
SetEvent(arhDBCEvent[i]); // Previous SQLDriverConnect() failed. No need to call SQLDisconnect().
}
}
WaitForMultipleObjects(NUMBER_OPERATIONS, arhDBCEvent, TRUE, INFINITE);
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (SQL_SUCCEEDED(arrcDBC[i]) )
{
SQLCompleteAsync(SQL_HANDLE_DBC, arhDbc[i], &arrcDBC[i]);; // To Complete
}
}
goto Cleanup;
}
// Statement Operations begin here
// Alloc statement handle
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc = SQLAllocHandle(SQL_HANDLE_STMT, arhDbc[i], &arhStmt[i]);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Enable STMT Async on all statement handles
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc = SQLSetStmtAttr(arhStmt[i], SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Create event objects
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
arhSTMTEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto-reset, initial state is not-signaled
if (!arhSTMTEvent[i]) goto Cleanup;
}
// Enable notification on all statement handles
// Event
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
rc= SQLSetStmtAttr(arhStmt[i], SQL_ATTR_ASYNC_STMT_EVENT, arhSTMTEvent[i], SQL_IS_POINTER);
if ( !SQL_SUCCEEDED(rc) ) goto Cleanup;
}
// Initiate SQLExecDirect() calls
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLExecDirect(arhStmt[i], (SQLTCHAR*)TEXT("select au_lname, au_fname from authors"), SQL_NTS);
}
// Can do some other staff before calling WaitForMultipleObjects
WaitForMultipleObjects(NUMBER_OPERATIONS, arhSTMTEvent, TRUE, INFINITE); // Wait All
// Now, call SQLCompleteAsync to complete the operation and get return code
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLCompleteAsync(SQL_HANDLE_STMT, arhStmt[i], &arrcSTMT[i]);
}
// Check return values
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if ( !SQL_SUCCEEDED(arrcSTMT[i]) ) goto Cleanup;
}
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
//Do some binding jobs here, set SQL_ATTR_ROW_ARRAY_SIZE
}
// Now, initiate fetching
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLFetch(arhStmt[i]);
}
// Can do some other staff before calling WaitForMultipleObjects
WaitForMultipleObjects(NUMBER_OPERATIONS, arhSTMTEvent, TRUE, INFINITE);
// Now, to complete the operations and get return code
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
SQLCompleteAsync(SQL_HANDLE_STMT, arhStmt[i], &arrcSTMT[i]);
}
// Check return code
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if ( !SQL_SUCCEEDED(arrcSTMT[i]) ) goto Cleanup;
}
// USE fetched data here!!
Cleanup:
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (arhStmt[NUMBER_OPERATIONS])
{
SQLFreeHandle(SQL_HANDLE_STMT, arhStmt[i]);
arhStmt[i] = NULL;
}
}
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (arhSTMTEvent[i])
{
CloseHandle(arhSTMTEvent[i]);
arhSTMTEvent[i] = NULL;
}
}
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (arhDbc[i])
{
SQLFreeHandle(SQL_HANDLE_DBC, arhDbc[i]);
arhDbc[i] = NULL;
}
}
for (int i=0; i<NUMBER_OPERATIONS; i++)
{
if (arhDBCEvent[i])
{
CloseHandle(arhDBCEvent[i]);
arhDBCEvent[i] = NULL;
}
}
if (hEnv)
{
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
hEnv = NULL;
}
return 0;
}
Определение того, поддерживает ли драйвер асинхронное уведомление
Приложение ODBC может определить, поддерживает ли драйвер ODBC асинхронное уведомление, вызвав SQLGetInfo. Диспетчер драйверов ODBC будет вызывать SQLGetInfo драйвера с SQL_ASYNC_NOTIFICATION.
SQLUINTEGER InfoValue;
SQLLEN cbInfoLength;
SQLRETURN retcode;
retcode = SQLGetInfo (hDbc,
SQL_ASYNC_NOTIFICATION,
&InfoValue,
sizeof(InfoValue),
NULL);
if (SQL_SUCCEEDED(retcode))
{
if (SQL_ASYNC_NOTIFICATION_CAPABLE == InfoValue)
{
// The driver supports asynchronous notification
}
else if (SQL_ASYNC_NOTIFICATION_NOT_CAPABLE == InfoValue)
{
// The driver does not support asynchronous notification
}
}
Связывание дескриптора событий Win32 с дескриптором ODBC
Приложения отвечают за создание объектов событий Win32 с помощью соответствующих функций Win32. Приложение может связать один дескриптор событий Win32 с одним дескриптором подключения ODBC или одним дескриптором инструкции ODBC.
атрибуты Подключение ion SQL_ATTR_ASYNC_DBC_FUNCTION_ENABLE и SQL_ATTR_ASYNC_DBC_EVENT определяют, выполняется ли ODBC в асинхронном режиме и включает ли ODBC режим уведомлений для дескриптора подключения. Атрибуты инструкции SQL_ATTR_ASYNC_ENABLE и SQL_ATTR_ASYNC_STMT_EVENT определяют, выполняется ли ODBC в асинхронном режиме и включает ли ODBC режим уведомлений для дескриптора инструкции.
SQL_ATTR_ASYNC_ENABLE или SQL_ATTR_ASYNC_DBC_FUNCTION_ENABLE | SQL_ATTR_ASYNC_STMT_EVENT или SQL_ATTR_ASYNC_DBC_EVENT | Режим |
---|---|---|
Enable | non-NULL | Асинхронное уведомление |
Enable | null | Асинхронное опрос |
Disable | любое | Синхронная |
Приложение может временно отключить асинхронный режим работы. ODBC игнорирует значения SQL_ATTR_ASYNC_DBC_EVENT если асинхронная операция уровня подключения отключена. ODBC игнорирует значения SQL_ATTR_ASYNC_STMT_EVENT если асинхронная операция уровня инструкции отключена.
Синхронный вызов SQLSetStmtAttr и SQLSet Подключение Attr
SQLSet Подключение Attr поддерживает асинхронные операции, но вызов SQLSet Подключение Attr для задания SQL_ATTR_ASYNC_DBC_EVENT всегда синхронен.
SQLSetStmtAttr не поддерживает асинхронное выполнение.
Сценарий ошибки
При вызове SQLSet Подключение Attr перед подключением диспетчер драйверов не может определить, какой драйвер следует использовать. Поэтому диспетчер драйверов возвращает успешное выполнение для SQLSet Подключение Attr, но атрибут может быть не готов к настройке в драйвере. Диспетчер драйверов устанавливает эти атрибуты, когда приложение вызывает функцию подключения. Диспетчер драйверов может ошибся, так как драйвер не поддерживает асинхронные операции.
Наследование атрибутов подключения
Как правило, операторы подключения наследуют атрибуты подключения. Однако атрибут SQL_ATTR_ASYNC_DBC_EVENT не наследуется и влияет только на операции подключения.
Чтобы связать дескриптор событий с дескриптором подключения ODBC, приложение ODBC вызывает НАБОР SQLSet ODBC Подключение Attr и указывает SQL_ATTR_ASYNC_DBC_EVENT в качестве атрибута и дескриптор события в качестве значения атрибута. Новый атрибут ODBC SQL_ATTR_ASYNC_DBC_EVENT имеет тип SQL_IS_POINTER.
HANDLE hEvent;
hEvent = CreateEvent(
NULL, // default security attributes
FALSE, // auto-reset event
FALSE, // initial state is non-signaled
NULL // no name
);
Как правило, приложения создают объекты событий автоматического сброса. ODBC не сбрасывает объект события. Приложения должны убедиться, что объект не находится в сигнальном состоянии, прежде чем вызывать асинхронную функцию ODBC.
SQLRETURN retcode;
retcode = SQLSetConnectAttr ( hDBC,
SQL_ATTR_ASYNC_DBC_EVENT, // Attribute name
(SQLPOINTER) hEvent, // Win32 Event handle
SQL_IS_POINTER); // Length Indicator
SQL_ATTR_ASYNC_DBC_EVENT — это атрибут только для диспетчера драйверов, который не будет задан в драйвере.
Значение по умолчанию SQL_ATTR_ASYNC_DBC_EVENT равно NULL. Если драйвер не поддерживает асинхронное уведомление, получение или настройка SQL_ATTR_ASYNC_DBC_EVENT вернет SQL_ERROR с помощью SQLSTATE HY092 (недопустимый идентификатор атрибута или параметра).
Если последнее значение SQL_ATTR_ASYNC_DBC_EVENT, заданное в дескрипторе подключения ODBC, не РАВНО NULL, а асинхронный режим включен приложению, задав атрибут SQL_ATTR_ASYNC_DBC_FUNCTION_ENABLE с SQL_ASYNC_DBC_ENABLE_ON, вызовите любую функцию подключения ODBC, поддерживающую асинхронный режим, получите уведомление о завершении. Если последнее значение SQL_ATTR_ASYNC_DBC_EVENT в дескрипторе подключения ODBC равно NULL, ODBC не отправляет приложению никаких уведомлений, независимо от того, включен ли асинхронный режим.
Приложение может задать SQL_ATTR_ASYNC_DBC_EVENT до или после установки SQL_ATTR_ASYNC_DBC_FUNCTION_ENABLE атрибута.
Приложения могут задать атрибут SQL_ATTR_ASYNC_DBC_EVENT для дескриптора подключения ODBC перед вызовом функции подключения (SQL Подключение, SQLBrowse Подключение или SQLDriver Подключение). Так как диспетчер драйверов ODBC не знает, какой драйвер ODBC будет использоваться приложением, он вернет SQL_SUCCESS. Когда приложение вызывает функцию подключения, диспетчер драйверов ODBC проверка поддерживает ли драйвер асинхронное уведомление. Если драйвер не поддерживает асинхронное уведомление, диспетчер драйверов ODBC возвращает SQL_ERROR с S1_118 SQLSTATE (драйвер не поддерживает асинхронное уведомление). Если драйвер поддерживает асинхронное уведомление, диспетчер драйверов ODBC вызовет драйвер и задает соответствующие атрибуты SQL_ATTR_ASYNC_DBC_NOTIFICATION_CALLBACK и SQL_ATTR_ASYNC_DBC_NOTIFICATION_CONTEXT.
Аналогичным образом приложение вызывает SQLSetStmtAttr в дескриптор инструкции ODBC и задает атрибут SQL_ATTR_ASYNC_STMT_EVENT для включения или отключения асинхронного уведомления уровня инструкции. Так как функция инструкции всегда вызывается после установки подключения, SQLSetStmtAttr возвращает SQL_ERROR с sqlSTATE S1_118 (драйвер не поддерживает асинхронное уведомление), если соответствующий драйвер не поддерживает асинхронные операции или драйвер поддерживает асинхронную операцию, но не поддерживает асинхронное уведомление.
SQLRETURN retcode;
retcode = SQLSetStmtAttr ( hSTMT,
SQL_ATTR_ASYNC_STMT_EVENT, // Attribute name
(SQLPOINTER) hEvent, // Win32 Event handle
SQL_IS_POINTER); // length Indicator
SQL_ATTR_ASYNC_STMT_EVENT, который может иметь значение NULL, является атрибутом только для диспетчера драйверов, который не будет задан в драйвере.
Значение по умолчанию SQL_ATTR_ASYNC_STMT_EVENT равно NULL. Если драйвер не поддерживает асинхронное уведомление, получение или настройка атрибута SQL_ATTR_ASYNC_ STMT_EVENT возвращает SQL_ERROR с помощью SQLSTATE HY092 (недопустимый идентификатор атрибута или параметра).
Приложение не должно связывать один и тот же дескриптор событий с несколькими дескрипторами ODBC. В противном случае одно уведомление будет потеряно, если вызовы двух асинхронных функций ODBC завершатся на двух дескрипторах, использующих один дескриптор событий. Чтобы избежать дескриптора инструкции, наследующего тот же дескриптор событий от дескриптора подключения, ODBC возвращает SQL_ERROR с помощью SQLSTATE IM016 (не удается установить атрибут инструкции в дескриптор подключения), если приложение задает SQL_ATTR_ASYNC_STMT_EVENT на дескриптор подключения.
Вызов асинхронных функций ODBC
После включения асинхронного уведомления и запуска асинхронной операции приложение может вызывать любую функцию ODBC. Если функция принадлежит набору функций, поддерживающих асинхронную операцию, приложение получит уведомление о завершении операции независимо от того, завершилась ли функция или завершилась успешно. Единственным исключением является то, что приложение вызывает функцию ODBC с недопустимым дескриптором подключения или инструкции. В этом случае ODBC не получит дескриптор события и присвоит ему сигнальное состояние.
Приложение должно убедиться, что связанный объект события находится в состоянии без сигнала, прежде чем запускать асинхронную операцию на соответствующем дескрипторе ODBC. ODBC не сбрасывает объект события.
Получение уведомлений из ODBC
Поток приложения может вызвать WaitForSingleObject , чтобы дождаться одного дескриптора события или вызова WaitForMultipleObjects , чтобы ждать в массиве дескрипторов событий и быть приостановлен до тех пор, пока один или все объекты событий будут сигнализировать или интервал времени ожидания.
DWORD dwStatus = WaitForSingleObject(
hEvent, // The event associated with the ODBC handle
5000 // timeout is 5000 millisecond
);
If (dwStatus == WAIT_TIMEOUT)
{
// time-out interval elapsed before all the events are signaled.
}
Else
{
// Call the corresponding Asynchronous ODBC API to complete all processing and retrieve the return code.
}