MySQL 8.4 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 中從 Socket 讀取的子程式不是中斷安全的。這樣做是考慮到您可能希望擁有自己的警報,可以中斷對伺服器的長時間讀取。如果您為 SIGPIPE 中斷安裝中斷處理常式,則 Socket 處理應為執行緒安全的。

為避免在連線終止時中止程式,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_thread_init(),然後再呼叫任何 MySQL 函式。(如果您呼叫 mysql_init(),它會為您呼叫 mysql_thread_init()。)

  3. 在執行緒中,在呼叫 pthread_exit() 之前呼叫 mysql_thread_end()。這會釋放 MySQL 執行緒特定變數使用的記憶體。

關於 mysql_init() 的上述注意事項也適用於 mysql_connect(),後者會呼叫 mysql_init()