异步执行(轮询方法)
在 ODBC 3.8 和 Windows 7 SDK 之前,只允许对语句函数执行异步操作。 有关详细信息,请参阅本主题后面的异步执行语句操作。
Windows 7 SDK 中的 ODBC 3.8 引入了对连接相关操作的异步执行。 有关详细信息,请参阅本主题后面的异步执行连接操作部分。
在 Windows 7 SDK 中,对于异步语句或连接操作,应用程序使用轮询方法确定异步操作已完成。 从 Windows 8 SDK 开始,可以使用通知方法确定异步操作已完成。 有关详细信息,请参阅异步执行(通知方法)。
默认情况下,驱动程序同步执行 ODBC 函数;也就是说,应用程序调用函数,而驱动程序在完成执行该函数之前不会将控制权返回给应用程序。 但是,某些函数可以异步执行;也就是说,应用程序调用函数,而驱动程序在经过最少的处理后,将控制权返回给应用程序。 然后,当第一个函数仍在执行时,应用程序可以调用其他函数。
大多数主要在数据源上执行的函数都支持异步执行,例如用于建立连接、准备和执行 SQL 语句、检索元数据、提取数据和提交事务的函数。 当在数据源上执行的任务(例如登录过程或针对大型数据库的复杂查询)需要很长时间时,此功能最有用。
当应用程序使用启用了异步处理的语句或连接执行函数时,驱动程序将执行最少量的处理(例如检查错误参数),手动处理数据源,并使用 SQL_STILL_EXECUTING 返回代码将控制权返回给应用程序。 应用程序随后可执行其他任务。 为了确定异步函数何时完成,应用程序会定期轮询驱动程序,方法是使用与最初使用的相同参数调用该函数。 如果函数仍在执行,则返回 SQL_STILL_EXECUTING;如果已完成执行,则返回同步执行时会返回的代码,例如 SQL_SUCCESS、SQL_ERROR 或 SQL_NEED_DATA。
函数以同步还是异步方式是特定于驱动程序的。 例如,假设结果集元数据缓存在驱动程序中。 在这种情况下,执行 SQLDescribeCol 需要很少的时间,驱动程序应该只执行函数,而不是人为地延迟执行。 另一方面,如果驱动程序需要从数据源检索元数据,则应在应用程序执行此操作时将控制权返回给应用程序。 因此,应用程序在首次异步执行函数时,必须能够处理除 SQL_STILL_EXECUTING 以外的返回代码。
异步执行语句操作
以下语句函数对数据源进行操作,可以异步执行:
SQLBulkOperations
SQLColAttribute
SQLColumnPrivileges
SQLColumns
SQLDescribeCol
SQLDescribeParam
SQLExecDirect
SQLExecute
SQLFetch
SQLFetchScroll
SQLForeignKeys
SQLGetData
SQLGetTypeInfo
SQLMoreResults
SQLNumParams
SQLNumResultCols
SQLParamData
SQLPrepare
SQLPrimaryKeys
SQLProcedureColumns
SQLProcedures
SQLPutData
SQLSetPos
SQLSpecialColumns
SQLStatistics
SQLTablePrivileges
SQLTables
基于每个语句或每个连接控制异步语句执行,具体取决于数据源。 也就是说,应用程序不会指定以异步方式执行特定函数,而是指定在特定语句上执行的任何函数都将以异步方式执行。 若要了解支持哪一项,应用程序会使用 SQL_ASYNC_MODE 选项调用 SQLGetInfo。 如果支持语句句柄的连接级异步执行,则返回 SQL_AM_CONNECTION;如果支持语句级异步执行,则返回 SQL_AM_STATEMENT。
若要指定使用特定语句执行的函数以异步方式执行,应用程序使用 SQL_ATTR_ASYNC_ENABLE 属性调用 SQLSetStmtAttr 并将其设置为 SQL_ASYNC_ENABLE_ON。 如果支持连接级异步处理,则 SQL_ATTR_ASYNC_ENABLE 语句为只读属性,值与为其分配语句的连接的连接属性相同。 无论语句属性的值是在语句分配时设置还是稍后设置的,该值都特定于驱动程序。 尝试设置该值将返回 SQL_ERROR 和 SQLSTATE HYC00(未实现可选功能)。
若要指定使用特定连接执行的函数以异步方式执行,应用程序使用 SQL_ATTR_ASYNC_ENABLE 属性调用SQLSetConnectAttr 并将其设置为 SQL_ASYNC_ENABLE_ON。 将为异步执行启用在连接上分配的所有未来语句句柄;由驱动程序定义此操作是否启用现有语句句柄。 如果将 SQL_ATTR_ASYNC_ENABLE 设置为 SQL_ASYNC_ENABLE_OFF,则连接上的所有语句都处于同步模式。 如果在连接上有活动语句时启用异步执行,则返回错误。
为了确定驱动程序可以在给定连接上支持的异步模式下的最大活动并发语句数,应用程序会使用 SQL_MAX_ASYNC_CONCURRENT_STATEMENTS 选项调用 SQLGetInfo。
以下代码演示轮询模型的工作原理:
SQLHSTMT hstmt1;
SQLRETURN rc;
// Specify that the statement is to be executed asynchronously.
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0);
// Execute a SELECT statement asynchronously.
while ((rc=SQLExecDirect(hstmt1,"SELECT * FROM Orders",SQL_NTS))==SQL_STILL_EXECUTING) {
// While the statement is still executing, do something else.
// Do not use hstmt1, because it is being used asynchronously.
}
// When the statement has finished executing, retrieve the results.
当函数以异步方式执行时,应用程序可以对任何其他语句调用函数。 应用程序还可以在任何连接上调用函数,但与异步语句关联的函数除外。 但是,在语句操作返回 SQL_STILL_EXECUTING 后,应用程序只调用原始函数和以下函数(使用语句句柄或其关联的连接、环境句柄):
SQLCancelHandle(语句句柄上)
例如,如果应用程序使用异步语句或与该语句关联的连接调用任何其他函数,则函数返回 SQLSTATE HY010 (函数序列错误)。
SQLHDBC hdbc1, hdbc2;
SQLHSTMT hstmt1, hstmt2, hstmt3;
SQLCHAR * SQLStatement = "SELECT * FROM Orders";
SQLUINTEGER InfoValue;
SQLRETURN rc;
SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);
SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt2);
SQLAllocHandle(SQL_HANDLE_STMT, hdbc2, &hstmt3);
// Specify that hstmt1 is to be executed asynchronously.
SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0);
// Execute hstmt1 asynchronously.
while ((rc = SQLExecDirect(hstmt1, SQLStatement, SQL_NTS)) == SQL_STILL_EXECUTING) {
// The following calls return HY010 because the previous call to
// SQLExecDirect is still executing asynchronously on hstmt1. The
// first call uses hstmt1 and the second call uses hdbc1, on which
// hstmt1 is allocated.
SQLExecDirect(hstmt1, SQLStatement, SQL_NTS); // Error!
SQLGetInfo(hdbc1, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL); // Error!
// The following calls do not return errors. They use a statement
// handle other than hstmt1 or a connection handle other than hdbc1.
SQLExecDirect(hstmt2, SQLStatement, SQL_NTS); // OK
SQLTables(hstmt3, NULL, 0, NULL, 0, NULL, 0, NULL, 0); // OK
SQLGetInfo(hdbc2, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL); // OK
}
当应用程序调用函数以确定该函数是否仍在异步执行时,必须使用原始语句句柄。 这是因为异步执行是按语句跟踪的。 应用程序还必须提供其他参数的有效值(原始参数即可),才能在驱动程序管理器中获取过去的错误检查。 但是,在驱动程序检查语句句柄并确定语句正以异步方式执行之后,会忽略所有其他参数。
当函数以异步方式执行时(即在函数返回 SQL_STILL_EXECUTING 之后以及返回其他代码之前),应用程序可以通过调用具有相同语句句柄的 SQLCancel 或 SQLCancelHandle 来取消该函数的执行。 这样并不能保证取消函数的执行。 例如,该函数可能已完成执行。 此外,SQLCancel 或 SQLCancelHandle 返回的代码仅指示取消函数的尝试是否成功,而不指示实际上是否取消了该函数。 为了确定函数是否已取消,应用程序将再次调用该函数。 如果函数已取消,则返回 SQL_ERROR 和 SQLSTATE HY008(操作已取消)。 如果该函数未取消,则返回另一个代码,例如 SQL_SUCCESS、SQL_STILL_EXECUTING,或具有不同 SQLSTATE 的 SQL_ERROR。
若要在驱动程序支持语句级异步处理时禁用特定语句的异步执行,应用程序会使用 SQL_ATTR_ASYNC_ENABLE 属性调用 SQLSetStmtAttr 并将其设置为 SQL_ASYNC_ENABLE_OFF。 如果驱动程序支持连接级异步处理,应用程序会调用 SQLSetConnectAttr 以将 SQL_ATTR_ASYNC_ENABLE 设置为 SQL_ASYNC_ENABLE_OFF,从而禁用连接上所有语句的异步执行。
应用程序应在原始函数的重复循环中处理诊断记录。 如果在执行异步函数时调用 SQLGetDiagField 或 SQLGetDiagRec,将返回诊断记录的当前列表。 每次重复原始函数调用时,都会清除以前的诊断记录。
异步执行连接操作
在 ODBC 3.8 之前,允许异步执行语句相关的操作(例如准备、执行和提取)以及目录元数据操作。 从 ODBC 3.8 开始,还可以异步执行连接相关的操作,例如连接、断开连接、提交和回滚。
有关 ODBC 3.8 的详细信息,请参阅 ODBC 3.8 中的新增功能。
在以下情况下,异步执行连接操作非常有用:
当少量线程以极高的数据速率管理大量设备时。 为了最大限度地提高响应能力和可伸缩性,所有操作都以异步方式执行。
如果要在多个连接上重叠数据库操作,以缩短传输时间。
高效的异步 ODBC 调用和取消连接操作的功能使应用程序能够允许用户取消任何缓慢的操作,而无需等待超时。
现在可以异步执行在连接句柄上运行的以下函数:
SQLDisconnect
SQLDriverConnect
为了确定驱动程序是否支持对这些函数执行异步操作,应用程序会使用 SQL_ASYNC_DBC_FUNCTIONS 调用 SQLGetInfo。 如果支持异步操作,则返回 SQL_ASYNC_DBC_CAPABLE。 如果不支持异步操作,则返回 SQL_ASYNC_DBC_NOT_CAPABLE。
为了指定以异步方式执行使用特定连接执行的函数,应用程序会使用 SQL_ATTR_ASYNC_ENABLE 属性调用SQLSetConnectAttr 并将其设置为 SQL_ASYNC_ENABLE_ON。 在建立连接之前设置连接属性的操作将始终同步执行。 此外,使用 SQLSetConnectAttr 设置连接属性 SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE 的操作始终同步执行。
应用程序可以在建立连接之前启用异步操作。 由于驱动程序管理器在建立连接之前无法确定要使用的驱动程序,因此驱动程序管理器将始终在 SQLSetConnectAttr 中返回成功。 但是,如果 ODBC 驱动程序不支持异步操作,可能无法连接。
通常,最多可以有一个与特定连接句柄或语句句柄关联的异步执行函数。 但是,连接句柄可以有多个关联的语句句柄。 如果在连接句柄上没有执行异步操作,则关联的语句句柄可以执行异步操作。 同样,如果在任何关联的语句句柄上没有正在进行异步操作,则可以在连接句柄上执行异步操作。 尝试使用当前正在执行异步操作的句柄执行异步操作将返回 HY010“函数序列错误”。
如果连接操作返回 SQL_STILL_EXECUTING,则应用程序只能为该连接句柄调用原始函数和以下函数:
SQLCancelHandle(连接句柄上)
SQLGetDiagField
SQLGetDiagRec
SQLAllocHandle(分配 ENV/DBC)
SQLAllocHandleStd(分配 ENV/DBC)
SQLGetEnvAttr
SQLGetConnectAttr
SQLDataSources
SQLDrivers
SQLGetInfo
SQLGetFunctions
应用程序应在原始函数的重复循环中处理诊断记录。 如果在执行异步函数时调用 SQLGetDiagField 或 SQLGetDiagRec,将返回诊断记录的当前列表。 每次重复原始函数调用时,都会清除以前的诊断记录。
如果正在异步打开或关闭连接,则当应用程序在原始函数调用中收到 SQL_SUCCESS 或 SQL_SUCCESS_WITH_INFO 时,操作将完成。
已向 ODBC 3.8 添加了一个新函数 SQLCancelHandle。 此函数会取消六个连接函数(SQLBrowseConnect、SQLConnect、SQLDisconnect、SQLDriverConnect、SQLEndTran 和 SQLSetConnectAttr)。 应用程序应调用 SQLGetFunctions 来确定驱动程序是否支持 SQLCancelHandle。 与 SQLCancel 一样,即使 SQLCancelHandle 返回成功,也并不意味着操作已取消。 应用程序应再次调用原始函数,以确定操作是否已取消。 SQLCancelHandle 允许取消连接句柄或语句句柄上的异步操作。 使用 SQLCancelHandle 取消语句句柄上的操作与调用 SQLCancel 相同。
不需要同时支持 SQLCancelHandle 和异步连接操作。 驱动程序可以支持异步连接操作,但不支持 SQLCancelHandle,反之亦然。
使用 ODBC 3.8 驱动程序和 ODBC 3.8 驱动程序管理器的 ODBC 3.x 和 ODBC 2.x 应用程序也可以使用异步连接操作和 SQLCancelHandle。 有关如何使旧应用程序能够使用更高 ODBC 版本中的新功能的信息,请参阅兼容性矩阵。
连接池
每当启用连接池时,最低限度仅支持以下异步操作:使用 SQLConnect 和 SQLDriverConnect 建立连接,以及关闭与 SQLDisconnect 的连接。 但是,应用程序仍应能够处理来自 SQLConnect、SQLDriverConnect 和 SQLDisconnect 的 SQL_STILL_EXECUTING 返回值。
启用连接池后,支持 SQLEndTran 和 SQLSetConnectAttr 执行异步操作。
示例
A. 启用连接函数的异步执行
以下示例演示如何使用 SQLSetConnectAttr 为连接相关函数启用异步执行。
BOOL AsyncConnect (SQLHANDLE hdbc)
{
SQLRETURN r;
SQLHANDLE hdbc;
// Enable asynchronous execution of connection functions.
// This must be executed synchronously, that is r != SQL_STILL_EXECUTING
r = SQLSetConnectAttr(
hdbc,
SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE,
reinterpret_cast<SQLPOINTER> (SQL_ASYNC_DBC_ENABLE_ON),
0);
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
{
return FALSE;
}
TCHAR szConnStrIn[256] = _T("DSN=AsyncDemo");
r = SQLDriverConnect(hdbc, NULL, (SQLTCHAR *) szConnStrIn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
if (r == SQL_ERROR)
{
// Use SQLGetDiagRec to process the error.
// If SQLState is HY114, the driver does not support asynchronous execution.
return FALSE;
}
while (r == SQL_STILL_EXECUTING)
{
// Do something else.
// Check for completion, with the same set of arguments.
r = SQLDriverConnect(hdbc, NULL, (SQLTCHAR *) szConnStrIn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
}
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
{
return FALSE;
}
return TRUE;
}
B. 异步提交操作
此示例演示异步提交操作。 也可以通过这种方式执行回滚操作。
BOOL AsyncCommit ()
{
SQLRETURN r;
// Assume that SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE is SQL_ASYNC_DBC_ENABLE_ON.
r = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
while (r == SQL_STILL_EXECUTING)
{
// Do something else.
// Check for completion with the same set of arguments.
r = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
}
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
{
return FALSE;
}
return TRUE;
}