2013-04-23 3 views
3

(xmlrpc-c를 기반으로하는) XMLRPC 서버에서 스레드는 다음 함수를 사용하여 일부 데이터를 검색하기 위해 MySQL 연결을 만들고자 할 수 있습니다 :MySQL Connector 코드가 C++ 인 스레드는 종료되지 않습니다

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

+0

스레드 내에서이 함수를 호출 할 때만 종료되지 않고 나머지 스레드 코드는 관련성이없는 것으로 보입니다. –

답변

4

솔루션을 검색하다가 sql::Driver::threadInit()sql::Driver::threadEnd()을 언급했습니다. 그러나 C++ Connector 1.0.5 버전에서 이러한 기능을 사용할 수 없었습니다. 드라이버 인스턴스를 얻은 후 driver->threadInit();을 추가하고 내 기능이 끝날 때 driver->threadEnd();을 추가하면이 문제가 해결되었습니다.

추가 드라이버 :: threadInit() 및 드라이버 :: threadEnd() 메소드 : 다음

MySQL's 1.1.0 change history에서이 스레드 초기화 및 종료 기능의 언급이다. 모든 스레드 클라이언트의 스레드는 Connector/C++ 이있는 다른 작업을 수행하기 전에 스레드의 시작 부분에서 Driver :: threadInit()을 호출해야하며 모든 스레드는 완료 될 때 Driver :: threadEnd()를 호출해야합니다. 은 examples/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. 예외가 발생하면 코드가 구조화 된 방식으로 메모리가 누출됩니다. 시도/catch 외부의 변수를 선언하고 (마지막으로 (또는 현지 해당))을 사용하여 적절하게 정리하거나 스마트 포인터 (사용 가능한 경우)을 사용하는 것이 좋습니다.

전화 또는 주변 코드를 표시하지 않으므로 실제로 진행되고있는 상황을 알기 어렵습니다. 쓰레드가 종료되었을 때 종료 코드를 확인합니까? 디버거에 연결하여 닫는 대신 수행중인 작업을 확인할 수 있습니까?

+1

답장을 보내 주셔서 감사합니다. sql :: Driver :: threadInit() 또는 sql :: Driver :: threadEnd()를 사용하지 않고 커넥터의 최신 버전을 사용하여 up2date하지 않은 문제를 발견했습니다. 누설 가능성에 대한 귀하의 다른 의견을 확인하겠습니다. –

+0

이것이 C++ 인 것처럼 보이지만 "finally"는이 언어로 존재하지 않습니다. 당신 말이 맞아요,이 코드는 예외가 발생하면 RAM을 누설하는 심각한 결함이 있습니다. nullptr 값을 사용하여 try 블록 외부에서 con, stmt 및 res를 정의하십시오. try 블록이 nullptr이 아닌 경우 역순으로 삭제하십시오. if (res! = nullptr) delete res; .... – BJovke

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 연결을 제대로 종료합니다 !!!

는 또한 : 당신이 (따라서 귀하의 MySQL 서버 프로세스를 추가) 프로그램/기능 측면에서 적절하게 연결하고 쿼리를 닫습니다 수없는 경우 2 다음 옵션을 고려하십시오 : 공식 매뉴얼을 https://docs.oracle.com/cd/E17952_01/connector-cpp-en/connector-cpp-en.pdf

대안을 읽을

1/모든 MySQL 시간 초과 매개 변수를 10 초로 설정하십시오. 이하 (예를 들면); 2/SHOW PROCESSLIST 스크립트를 작성하고 SLEEP에있는 프로세스를 너무 오래 삭제하십시오.

건배.

+1

이것은 필요하지 않습니다. res-> close(); stmt-> close(); con-> close(); 이러한 개체의 소멸자가 대신 처리합니다. – BJovke

+0

실제로 close()가 필요하지 않습니다. 하지만 왜 죄수를 삭제해야합니까? 연결을 유지하려면 어떻게해야합니까? –