MySQL 9.0 C API 開發人員指南  /  C API 非同步介面  /  C API 非同步介面概觀

7.1 C API 非同步介面概觀

本節說明如何使用 C API 非同步介面。在此討論中,非同步 (asynchronous) 和非阻塞 (nonblocking) 會視為同義詞,同步 (synchronous) 和阻塞 (blocking) 亦然。

非同步 C API 函式涵蓋可能在讀取或寫入伺服器連線時發生阻塞的操作:初始連線操作、傳送查詢、讀取結果等等。每個非同步函式都有與其同步對應函式相同的名稱,並加上 _nonblocking 後綴。

如果有些操作不需要以非同步方式完成,或者非同步函式不適用,應用程式可以混合使用非同步和同步函式。

以下討論更詳細地說明如何使用非同步 C API 函式。

非同步函式呼叫慣例

所有非同步 C API 函式都會傳回 enum net_async_status 值。傳回值可以是下列其中一個值,表示操作狀態:

  • NET_ASYNC_NOT_READY:操作仍在進行中,尚未完成。

  • NET_ASYNC_COMPLETE:操作已成功完成。

  • NET_ASYNC_ERROR:操作因錯誤而終止。

  • NET_ASYNC_COMPLETE_NO_MORE_RESULTS:操作已成功完成,且沒有更多結果可用。此狀態僅適用於 mysql_next_result_nonblocking()

一般而言,要使用非同步函式,請執行以下操作:

  • 重複呼叫函式,直到它不再傳回 NET_ASYNC_NOT_READY 狀態。

  • 檢查最終狀態是否表示成功完成 (NET_ASYNC_COMPLETE) 或錯誤 (NET_ASYNC_ERROR)。

以下範例說明一些典型的呼叫模式。function(args) 表示非同步函式及其引數清單。

  • 如果需要在操作進行期間執行其他處理:

    enum net_async_status status;
    
    status = function(args);
    while (status == NET_ASYNC_NOT_READY) {
      /* perform other processing */
      other_processing ();
      /* invoke same function and arguments again */
      status = function(args);
    }
    if (status == NET_ASYNC_ERROR) {
      /* call failed; handle error */
    } else {
      /* call successful; handle result */
    }
  • 如果不需要在操作進行期間執行其他處理:

    enum net_async_status status;
    
    while ((status = function(args)) == NET_ASYNC_NOT_READY)
      ; /* empty loop */
    if (status == NET_ASYNC_ERROR) {
      /* call failed; handle error */
    } else {
      /* call successful; handle result */
    }
  • 如果函式成功/失敗結果並不重要,而且您只想確保操作已完成:

    while (function (args) != NET_ASYNC_COMPLETE)
      ; /* empty loop */

對於 mysql_next_result_nonblocking(),也需要考慮 NET_ASYNC_COMPLETE_NO_MORE_RESULTS 狀態,表示操作已成功完成,且沒有更多結果可用。使用方式如下:

while ((status = mysql_next_result_nonblocking()) != NET_ASYNC_COMPLETE) {
  if (status == NET_ASYNC_COMPLETE_NO_MORE_RESULTS) {
    /* no more results */
  }
  else if (status == NET_ASYNC_ERROR) {
    /* handle error by calling mysql_error(); */
    break;
  }
}

在大多數情況下,非同步函式的引數與對應同步函式的引數相同。例外情況是 mysql_fetch_row_nonblocking()mysql_store_result_nonblocking(),每個函式都比其同步對應函式多一個引數。如需詳細資訊,請參閱第 7.4.1 節,「mysql_fetch_row_nonblocking()」第 7.4.8 節,「mysql_store_result_nonblocking()」

範例程式

本節顯示一個範例 C++ 程式,說明如何使用非同步 C API 函式。

若要設定程式使用的 SQL 物件,請執行下列語句。視需要取代不同的資料庫或使用者;在此情況下,您也需要對程式進行一些調整。

CREATE DATABASE db;
USE db;
CREATE TABLE test_table (id INT NOT NULL);
INSERT INTO test_table VALUES (10), (20), (30);

CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'testpass';
GRANT ALL ON db.* TO 'testuser'@'localhost';

建立一個名為 async_app.cc 的檔案,其中包含下列程式。視需要調整連線參數。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <mysql.h>
#include <mysqld_error.h>

using namespace std;

/* change following connection parameters as necessary */
static const char * c_host = "localhost";
static const char * c_user = "testuser";
static const char * c_auth = "testpass";
static int          c_port = 3306;
static const char * c_sock = "/usr/local/mysql/mysql.sock";
static const char * c_dbnm = "db";

void perform_arithmetic() {
  cout<<"dummy function invoked\n";
  for (int i = 0; i < 1000; i++)
    i*i;
}

int main(int argc, char ** argv)
{
  MYSQL *mysql_local;
  MYSQL_RES *result;
  MYSQL_ROW row;
  net_async_status status;
  const char *stmt_text;

  if (!(mysql_local = mysql_init(NULL))) {
    cout<<"mysql_init() failed\n";
    exit(1);
  }
  while ((status = mysql_real_connect_nonblocking(mysql_local, c_host, c_user,
                                                  c_auth, c_dbnm, c_port,
                                                  c_sock, 0))
            == NET_ASYNC_NOT_READY)
    ; /* empty loop */
  if (status == NET_ASYNC_ERROR) {
    cout<<"mysql_real_connect_nonblocking() failed\n";
    exit(1);
  }

  /* run query asynchronously */
  stmt_text = "SELECT * FROM test_table ORDER BY id";
  status = mysql_real_query_nonblocking(mysql_local, stmt_text,
                                        (unsigned long)strlen(stmt_text));
  /* do some other task before checking function result */
  perform_arithmetic();
  while (status == NET_ASYNC_NOT_READY) {
    status = mysql_real_query_nonblocking(mysql_local, stmt_text,
                                          (unsigned long)strlen(stmt_text));
    perform_arithmetic();
  }
  if (status == NET_ASYNC_ERROR) {
    cout<<"mysql_real_query_nonblocking() failed\n";
    exit(1);
  }

  /* retrieve query result asynchronously */
  status = mysql_store_result_nonblocking(mysql_local, &result);
  /* do some other task before checking function result */
  perform_arithmetic();
  while (status == NET_ASYNC_NOT_READY) {
    status = mysql_store_result_nonblocking(mysql_local, &result);
    perform_arithmetic();
  }
  if (status == NET_ASYNC_ERROR) {
    cout<<"mysql_store_result_nonblocking() failed\n";
    exit(1);
  }
  if (result == NULL) {
    cout<<"mysql_store_result_nonblocking() found 0 records\n";
    exit(1);
  }

  /* fetch a row synchronously */
  row = mysql_fetch_row(result);
  if (row != NULL && strcmp(row[0], "10") == 0)
    cout<<"ROW: " << row[0] << "\n";
  else
    cout<<"incorrect result fetched\n";

  /* fetch a row asynchronously, but without doing other work */
  while (mysql_fetch_row_nonblocking(result, &row) != NET_ASYNC_COMPLETE)
    ; /* empty loop */
  /* 2nd row fetched */
  if (row != NULL && strcmp(row[0], "20") == 0)
    cout<<"ROW: " << row[0] << "\n";
  else
    cout<<"incorrect result fetched\n";

  /* fetch a row asynchronously, doing other work while waiting */
  status = mysql_fetch_row_nonblocking(result, &row);
  /* do some other task before checking function result */
  perform_arithmetic();
  while (status != NET_ASYNC_COMPLETE) {
    status = mysql_fetch_row_nonblocking(result, &row);
    perform_arithmetic();
  }
  /* 3rd row fetched */
  if (row != NULL && strcmp(row[0], "30") == 0)
    cout<<"ROW: " << row[0] << "\n";
  else
    cout<<"incorrect result fetched\n";

  /* fetch a row asynchronously (no more rows expected) */
  while ((status = mysql_fetch_row_nonblocking(result, &row))
           != NET_ASYNC_COMPLETE)
    ; /* empty loop */
  if (row == NULL)
    cout <<"No more rows to process.\n";
  else
    cout <<"More rows found than expected.\n";

  /* free result set memory asynchronously */
  while (mysql_free_result_nonblocking(result) != NET_ASYNC_COMPLETE)
    ; /* empty loop */

  mysql_close(mysql_local);
}

使用類似以下的命令編譯程式;視需要調整編譯器和選項:

gcc -g async_app.cc -std=c++11 \
  -I/usr/local/mysql/include \
  -o async_app -L/usr/lib64/ -lstdc++ \
  -L/usr/local/mysql/lib/ -lmysqlclient

執行程式。結果應類似於您在此處看到的內容,儘管您可能會看到不同數量的 dummy function invoked 實例。

dummy function invoked
dummy function invoked
ROW: 10
ROW: 20
dummy function invoked
ROW: 30
No more rows to process.

若要實驗程式,請從 test_table 新增和移除列,並在每次變更後再次執行程式。

非同步函式限制

以下限制適用於非同步 C API 函式的使用:

  • mysql_real_connect_nonblocking() 只能用於使用 sha256_passwordcaching_sha2_password 驗證的身分。

  • mysql_real_connect_nonblocking() 只能用於建立 TCP/IP 或 Unix Socket 檔案連線。

  • 不支援這些語句,而且必須使用同步 C API 函式處理:LOAD DATALOAD XML

  • 傳遞至啟動非阻塞操作的非同步 C API 呼叫的輸入引數,可能會持續使用到操作稍後終止為止,而且在終止發生之前不應重複使用。

  • 非同步 C API 函式不支援協定壓縮。