在 Client/Server 架構下,ODBC 2.0 提供同步與非同步兩種執行模式。同步模式邏輯直觀但易導致介面凍結與死鎖;非同步模式提升回應性卻大幅增加程式複雜度。本文從原理、優劣勢、適用場景、設定方法到實作模式,系統剖析兩種模式的權衡與最佳實務。
SQLExecDirect)→ 驅動程式封包發送至伺服器 → 阻塞等待 → 伺服器回傳結果 → API 返回。SQLSetConnectAttr(hdbc, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0)。SQL_STILL_EXECUTING,執行緒可繼續執行其他工作。RETCODE rc;
do {
rc = SQLExecDirect(hstmt, (SQLCHAR*)sql, SQL_NTS);
if (rc == SQL_STILL_EXECUTING) {
// 可在此處理 UI 訊息、更新進度條、檢查取消旗標
::Sleep(50); // 簡易輪詢,實際建議用 WaitForSingleObject + 事件
}
} while (rc == SQL_STILL_EXECUTING);
| 場景 | 推薦模式 | 理由 |
|---|---|---|
| 簡單查詢、小資料量、本地資料庫 | 同步 | 開發效率最高,阻塞時間可忽略 |
| 複雜報表、大量 UPDATE/DELETE、遠端伺服器 | 非同步 | 避免 UI 凍結,支援進度與取消 |
| 批次作業、背景處理、無 UI | 同步 + 獨立執行緒 | 邏輯簡單,利用 OS 排程 |
| 高併發、長交易、需精確鎖控制 | 非同步 + 連線池 | 最大化吞吐量 |
enum AsyncState { IDLE, EXECUTING, FETCHING, COMPLETED, FAILED, CANCELLED };
class CODBCAsyncExecutor {
HDBC m_hdbc; HSTMT m_hstmt;
AsyncState m_state;
CString m_sql;
CWnd* m_pNotifyWnd; // 完成時通知的視窗
UINT m_nMsg; // 自訂訊息 WM_ODBC_ASYNC_DONE
public:
BOOL ExecuteAsync(CWnd* pWnd, UINT msg, const CString& sql) {
if (m_state != IDLE) return FALSE;
m_pNotifyWnd = pWnd; m_nMsg = msg; m_sql = sql;
m_state = EXECUTING;
RETCODE rc = SQLExecDirect(m_hstmt, (SQLCHAR*)(LPCTSTR)m_sql, SQL_NTS);
if (rc == SQL_STILL_EXECUTING) {
SetTimer(1, 50, NULL); // 啟動輪詢定時器
return TRUE;
}
// 同步完成或錯誤
HandleResult(rc);
return (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO);
}
void OnTimer() {
if (m_state != EXECUTING) return;
RETCODE rc = SQLExecDirect(m_hstmt, (SQLCHAR*)(LPCTSTR)m_sql, SQL_NTS);
if (rc != SQL_STILL_EXECUTING) {
KillTimer(1);
HandleResult(rc);
}
}
void Cancel() {
if (m_state == EXECUTING) {
SQLCancel(m_hstmt);
m_state = CANCELLED;
KillTimer(1);
}
}
private:
void HandleResult(RETCODE rc) {
m_state = (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) ? COMPLETED : FAILED;
if (m_pNotifyWnd) m_pNotifyWnd->PostMessage(m_nMsg, (WPARAM)rc, (LPARAM)this);
}
};
// 視圖標頭
#define WM_ODBC_ASYNC_DONE (WM_USER + 100)
afx_msg LRESULT OnOdbcAsyncDone(WPARAM wParam, LPARAM lParam);
// 訊息映射
ON_MESSAGE(WM_ODBC_ASYNC_DONE, OnOdbcAsyncDone)
// 實作
LRESULT CMyView::OnOdbcAsyncDone(WPARAM wParam, LPARAM lParam) {
CODBCAsyncExecutor* pExec = (CODBCAsyncExecutor*)lParam;
RETCODE rc = (RETCODE)wParam;
if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
// 綁定結果集、填充 UI
BindAndDisplay(pExec->GetStmt());
} else {
ShowOdbcError(rc);
}
return 0;
}
ODBC 3.5 引入 SQLSetConnectAttr(..., SQL_ATTR_ASYNC_NOTIFICATION, ...),支援 Windows 事件物件或回調函式,避免輪詢。配合 WaitForMultipleObjects 可在工作者執行緒高效等待多個非同步操作。
同步模式適合簡單、快速、低併發場景;非同步模式是高回應性、長耗時操作的必然選擇。關鍵在於封裝非同步狀態機,將輪詢/通知、取消、錯誤處理內聚至專用類別,讓上層業務邏輯保持清晰。現代開發若環境允許,建議優先考慮 ADO 非同步 或 ODBC 3.5+ 事件通知,進一步降低複雜度。