MySQL 8.4 C API 开发人员指南  /  C API 异步接口  /  C API 异步接口概述

7.1 C API 异步接口概述

本节介绍如何使用 C API 异步接口。在本讨论中,异步和非阻塞被用作同义词,同步和阻塞也是如此。

异步 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() 只能用于使用以下身份验证插件之一进行身份验证的帐户:mysql_native_password (已弃用)、sha256_passwordcaching_sha2_password

  • mysql_real_connect_nonblocking() 只能用于建立 TCP/IP 或 Unix 套接字文件连接。

  • 以下语句不受支持,必须使用同步 C API 函数进行处理:LOAD DATALOAD XML

  • 传递给启动非阻塞操作的异步 C API 调用的输入参数可能在操作在稍后终止之前一直处于使用状态,并且在终止发生之前不应重用。

  • 异步 C API 函数不支持协议压缩。