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()