2013-04-23 45 views
3

在這我工作的XMLRPC服務器(基於XML-RPC關閉-C)的線程可能想使一個MySQL連接來獲取一些數據,使用下面的函數:線程在C++ MySQL連接代碼不結束

Distribution getEntitySetFromMysql(int id) { 

    Distribution result; 

    try { 
     sql::Driver *driver = get_driver_instance(); 
     sql::Connection *con = driver->connect((std::string)DBHOST, (std::string)USER, (std::string)PASSWORD); 
     con->setSchema((std::string)DATABASE); 

     sql::Statement *stmt = con->createStatement(); 
     std::stringstream query; 
     query << "SELECT concept_id, weight FROM entity_set_lines WHERE entity_set_id = " << id; 
     sql::ResultSet *res = stmt->executeQuery (query.str()); 

     while (res->next()) { 
      result[ res->getInt("concept_id") ] = res->getDouble("weight"); 
     } 

     delete res; 
     delete stmt; 
     con->close(); 
     delete con; 

    } catch (sql::SQLException &e) { 
     std::cout << "ERROR: SQLException in " << __FILE__; 
     std::cout << " (" << __func__<< ") on line " << __LINE__ << std::endl; 
     std::cout << "ERROR: " << e.what(); 
     std::cout << " (MySQL error code: " << e.getErrorCode(); 
     std::cout << ", SQLState: " << e.getSQLState() << ")" << std::endl; 

     if (e.getErrorCode() == 1047) { 
      std::cout << "\nYour server does not seem to support Prepared Statements at all. "; 
      std::cout << "Perhaps MYSQL < 4.1?" << std::endl; 
     } 

    } catch (std::runtime_error &e) { 

     std::cout << "ERROR: runtime_error in " << __FILE__; 
     std::cout << " (" << __func__ << ") on line " << __LINE__ << std::endl; 
     std::cout << "ERROR: " << e.what() << std::endl; 

    } 

    return result; 
} 

所有工作正常,但一個線程後運行該代碼,併成功返回其結果,螺紋仍懸而不會退出。這種方法有什麼問題?這有多基本錯誤? MySQL連接器線程安全嗎?

+0

這只是一個函數,調用這個函數的線程是做什麼的? – nos 2013-04-23 09:09:00

+0

只有當我從一個線程中調用這個函數時,它不會退出,線程代碼的其餘部分似乎是不相關的。 – 2013-04-23 09:17:15

回答

4

雖然谷歌搜索周圍的解決方案,我碰到的sql::Driver::threadInit()sql::Driver::threadEnd()提到。然而,正如我在C++連接器的1.0.5版本中那樣,這些功能對我來說是不可用的。在我的函數結束得到一個驅動程序實例和driver->threadEnd();後添加driver->threadInit();,這個問題就解決了。

下面是這個線程init和高端功能的MySQL's 1.1.0 change history提到:

添加的驅動程序:: threadInit()和驅動程序:: threadEnd()方法。每 螺紋的螺紋客戶必須在非常 啓動線程的調用驅動程序:: threadInit()它任何其他與連接器前/ C++ 和每一個線程必須調用驅動程序:: threadEnd當它完成()。您可以在示例/ pthreads.cpp中找到一個演示示例的示例。它強烈建議不要在線程之間共享連接。這是 理論上是可能的,如果設置一定的(無證)互斥,但 它不被支持。每個線程使用一個連接。沒有 兩個線程同時使用相同的連接。請查閱 有關MySQL手冊中線程的C API說明。連接器/ C++將C API封裝爲 。 (Lawrin,安德烈,烏爾夫)

TL; DR:如果你遇到這個問題,請確保您的C++ MySQL連接的版本> = 1.1.0,使用sql::Driver::threadInit()sql::Driver::threadEnd()方法來包圍你的連接代碼。

0

兩個想法:

  1. 的libmysql不是完全thread safe
  2. 你的代碼的結構,如果發生異常,你會泄漏內存的方式。你可以通過任何聲明你的變量的try/catch之外,使用終於(或等值當地貨幣),以確保適當的清理或使用智能指針(如果可用)得到更好的服務。

由於您不顯示任何調用或周圍的代碼,很難說實際發生了什麼。當它被認爲是完成時,你檢查線程的退出代碼嗎?你可以將它附加在調試器中,看看它在做什麼而不是關閉?

+1

感謝您的回覆。我發現問題在於我沒有使用sql :: Driver :: threadInit()或sql :: Driver :: threadEnd(),並且沒有使用最新版本的連接器進行up2date操作。我會檢查你的其他評論以及可能的泄漏。 – 2013-04-23 13:07:26

+0

如果這看起來像是C++,那麼「finally」在這種語言中不存在。你是對的,這個代碼有一個嚴重的缺陷,如果發生異常,它會泄漏內存。 使用nullptr值定義con,stmt和res之外的try塊。在嘗試塊之後,如果它們不是nullptr,則按相反順序刪除它們:if(res!= nullptr)delete res; ... – BJovke 2016-05-15 15:14:10

0

其實:

請勿使用:sql::Driver::threadInit()sql::Driver::threadEnd()

,因爲:你已經在使用try()

您忘記:

res->close(); 
stmt->close(); 
con->close(); 

delete res; 
delete stmt; 
delete con; 

例:

int connection_and_query_func() 
{ 
    /*connection and query variables*/ 
    sql::Driver *driver; 
    sql::Connection *con; 
    sql::Statement *stmt; 
    sql::ResultSet *res; 
    int err_exception_getErrorCode=0; 

    /*results variables*/ 
    int my_int_from_column_1 = 0; 
    double my_double_from_column_2 = 0; 
    .... 
    std:string my_string_from_column_p = ""; 

try 
    { 
     /* Create a connection */ 
     driver = get_driver_instance(); 
     con = driver->connect("address_name", "user_name", "password"); 

     /* Connect to the MySQL database */ 
     con->setSchema("schema_name"); 

     /* Execute MySQL Query*/ 
     stmt = con->createStatement(); 
     res = stmt->executeQuery("your query statement here"); 

     /* Read MySQL Query results per column*/ 
     my_int_from_column_1 = res->getInt(1); 
     my_double_from_column_2 = res->getDouble(2); 
     .... 
     my_string_from_column_p = res->getString(p); 

     /* Close MySQL Connection*/ 
     res->close(); 
     stmt->close(); 
     con->close(); 

     delete res; 
     delete stmt; 
     delete con; 
    }; 

/* Get last error*/ 
catch (sql::SQLException &exception) 
    { 
     err_exception_getErrorCode = exception.getErrorCode(); 
    }; 

return(0); 
}; 

結論:可以根據需要多次執行此操作。函數示例(connection_and_query_func())在完成後會正確關閉MySQL連接 - 不會將進程添加到MySQL服務器中!

此外:閱讀官方手冊https://docs.oracle.com/cd/E17952_01/connector-cpp-en/connector-cpp-en.pdf

替代方案:如果你不能關閉從程序/函數方面的正確的連接和查詢(因此增加了流程,你的MySQL服務器)考慮2個以下選項:

1 /將所有MySQL超時參數設置爲10秒。或更少(例如); 2 /編寫SHOW PROCESSLIST並刪除處於睡眠狀態的進程時間過長的腳本。

乾杯。

+1

這不是必需的: res-> close(); stmt-> close();關閉(); 這些對象的析構函數會爲你做這件事。 – BJovke 2016-05-15 15:08:36

+0

確實你不需要關閉()。但是爲什麼要刪除con?如果你想保持連接打開怎麼辦? – 2018-01-24 05:06:48