MySQL 9.0 C API 开发者指南  /  编写基于 C API 的客户端应用程序  /  编写 C API 多线程客户端程序

3.4 编写 C API 多线程客户端程序

本节提供了编写使用 MySQL C API 中线程相关函数的客户端程序的指南。有关这些函数的更多信息,请参阅 第 8.2 节“C API 线程函数说明”。有关使用它们的源代码示例,请查看 MySQL 源代码发行版中的 client 目录。

  • mysqlimport 的源代码在与 --use-threads 选项关联的代码中使用了线程。

  • mysqlslap 的源代码使用线程来设置并发工作负载,以测试服务器在高负载下的运行情况。

作为线程编程的替代方法,应用程序可能会发现异步(非阻塞)C API 函数很有用。这些函数使应用程序能够向服务器提交多个未完成的请求,并使用轮询确定每个请求何时完成。有关更多信息,请参阅 第 7 章“C API 异步接口”

如果在将线程程序链接到 MySQL 客户端库时出现未定义引用错误,则最可能的原因是您没有在链接/编译命令中包含线程库。

客户端库几乎是线程安全的。最大的问题是从套接字读取数据的 sql/net_serv.cc 中的子例程不是中断安全的。这样做是考虑到您可能希望拥有自己的警报,可以在长时间读取服务器时中断读取。如果您为 SIGPIPE 中断安装了中断处理程序,则套接字处理应该是线程安全的。

为了避免在连接终止时中止程序,MySQL 在第一次调用 mysql_library_init()mysql_init()mysql_connect() 时会阻塞 SIGPIPE。要使用您自己的 SIGPIPE 处理程序,请先调用 mysql_library_init(),然后安装您的处理程序。

客户端库在每个连接上都是线程安全的。两个线程可以共享同一个连接,但需要注意以下几点:

  • 除非您使用前面提到的异步 C API 函数,否则多个线程不能在同一个连接上同时向 MySQL 服务器发送查询。特别是,您必须确保在一个线程中调用 mysql_real_query()(或 mysql_query())和 mysql_store_result() 之间,没有其他线程使用同一个连接。为此,请在您的 mysql_real_query()(或 mysql_query())和 mysql_store_result() 调用对周围使用互斥锁。在 mysql_store_result() 返回后,可以释放锁,其他线程可以查询同一个连接。

    如果您使用 POSIX 线程,则可以使用 pthread_mutex_lock()pthread_mutex_unlock() 来建立和释放互斥锁。

    注意

    如果您查看 MySQL 源代码发行版中的程序,您会看到调用 native_mutex_lock()native_mutex_unlock(),而不是调用 pthread_mutex_lock()pthread_mutex_unlock()。后两个函数在 thr_mutex.h 头文件中定义,并映射到特定于平台的互斥锁函数。

  • 多个线程可以访问使用 mysql_store_result() 检索的不同结果集。

  • 要使用 mysql_use_result(),您必须确保在结果集关闭之前没有其他线程使用同一个连接。但是,对于共享同一个连接的线程客户端来说,最好使用 mysql_store_result()

如果一个线程没有创建到 MySQL 数据库的连接,而是调用 MySQL 函数,请考虑以下因素:

当您调用 mysql_init() 时,MySQL 会为调试库(以及其他用途)使用的线程创建一个线程特定的变量。如果您在线程调用 mysql_init() 之前调用 MySQL 函数,则该线程没有必要的线程特定的变量,您很可能会迟早遇到核心转储。为了避免问题,您必须执行以下操作:

  1. 在任何其他 MySQL 函数之前调用 mysql_library_init()。它不是线程安全的,因此请在创建线程之前调用它,或者使用互斥锁保护调用。

  2. 安排在调用任何 MySQL 函数之前,在线程处理程序的早期调用 mysql_thread_init()。(如果您调用 mysql_init(),它会为您调用 mysql_thread_init()。)

  3. 在线程中,在调用 pthread_exit() 之前调用 mysql_thread_end()。这将释放 MySQL 线程特定的变量使用的内存。

前面关于 mysql_init() 的说明也适用于 mysql_connect(),后者会调用 mysql_init()