非同步執行 (輪詢方法)

在 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 以外的傳回碼。

以非同步方式執行陳述式作業

下列陳述式函式會在資料來源上執行,並且可以非同步執行:

視資料來源而定,非同步陳述式執行是依個別陳述式或個別連線為基礎進行控制。 也就是說,應用程式不會指定以非同步方式執行特定函式,而是以非同步方式執行特定陳述式上所執行的任何函式。 為了找出支援哪個基礎,應用程式會使用 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 屬性呼叫 SQLSetStmtAttr,並將其設定為 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 之後,呼叫原始函式和下列函式 (使用陳述式控制代碼或其相關聯的連線、環境控制代碼):

舉例來說,如果應用程式使用非同步陳述式或與該陳述式相關聯的連線呼叫任何其他函式,則函式會傳回 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 之後,以及傳回不同程式碼之前),應用程式可以使用相同陳述式控制代碼呼叫 SQLCancelSQLCancelHandle 加以取消。 我們無法保證一定能取消函式執行。 舉例來說,函式有可能已經完成。 此外,SQLCancelSQLCancelHandle 所傳回的程式碼僅表示嘗試取消函式是否成功,而非實際上是否已取消函式。 若要判斷是否已取消函式,應用程式會再次呼叫函式。 如果已取消函式,則會傳回 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,從而停用連線上所有陳述式的非同步執行。

應用程式須在原始函式的重複迴圈中處理診斷記錄。 如果在非同步函式執行時呼叫 SQLGetDiagFieldSQLGetDiagRec,則會傳回目前的診斷記錄清單。 每次重複原始函式呼叫時,都會清除先前的診斷記錄。

以非同步方式執行連線作業

在 ODBC 3.8 之前,允許非同步執行陳述式相關作業 (例如準備、執行和擷取) 以及目錄中繼資料作業。 從 ODBC 3.8 開始,也支援非同步執行連線相關作業,例如連線、中斷連線、認可和復原。

如需 ODBC 3.8 的詳細資訊,請參閱 ODBC 3.8 的新功能

在下列案例中,以非同步方式執行連線作業相當實用:

  • 當少數執行緒管理大量極高資料速率的裝置時。 若要將回應性和延展性最大化,所有作業都應該採非同步方式。

  • 當您想要在多個連線上重疊資料庫作業,以減少耗用傳輸時間時。

  • 有效率的非同步 ODBC 呼叫和取消連線作業的能力,可支援應用程式讓使用者取消任何緩慢作業,而無須等待逾時。

下列在連線控制代碼上運作的函式,現在可以非同步方式執行:

若要判斷驅動程式是否支援這些函式的非同步作業,應用程式會使用 SQL_ASYNC_DBC_FUNCTIONS 呼叫 SQLGetInfo。 如果支援非同步作業,則會傳回 SQL_ASYNC_DBC_CAPABLE。 如果不支援非同步作業,則會傳回 SQL_ASYNC_DBC_NOT_CAPABLE。

針對使用特定連線執行的函式,若要指定以非同步方式執行,應用程式會呼叫 SQLSetStmtAttr,並將 SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE 屬性設定為 SQL_ASYNC_DBC_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。 此函式會取消六個連線函式 (SQLBrowseConnectSQLConnectSQLDisconnectSQLDriverConnectSQLEndTranSQLSetConnectAttr)。 應用程式須呼叫 SQLGetFunctions,以判斷驅動程式是否支援 SQLCancelHandle。 如同 SQLCancel,即使 SQLCancelHandle 傳回成功,也不表示已取消作業。 應用程式須再次呼叫原始函式,以判斷是否已取消作業。 SQLCancelHandle 可讓您取消連線控制代碼或陳述式控制代碼上的非同步作業。 使用 SQLCancelHandle 取消陳述式控制代碼上的作業,與呼叫 SQLCancel 相同。

無須同時支援 SQLCancelHandle 和非同步連線作業。 驅動程式可以支援非同步連線作業,但不支援 SQLCancelHandle,反之亦然。

ODBC 3.x 和 ODBC 2.x 應用程式也可以搭配 ODBC 3.8 驅動程式與 ODBC 3.8 驅動程式管理員,使用非同步連線作業和 SQLCancelHandle。 如需如何讓舊版應用程式使用更新版本 ODBC 中新功能的詳細資訊,請參閱相容性矩陣

連接共用

每當啟用連線共用時,非同步作業僅最低限度地支援使用 SQLConnectSQLDriverConnect 建立連線,以及使用 SQLDisconnect 關閉連線。 但應用程式仍應能處理 SQLConnect、SQLDriverConnectSQLDisconnect 的 SQL_STILL_EXECUTING 傳回值。

啟用連線共用時,非同步作業支援 SQLEndTranSQLSetConnectAttr

範例

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;  
}  

另請參閱

執行陳述式 ODBC