在C ++中具有MySQL连接器代码的线程不会结束

时间:2013-04-23 09:06:21

标签: mysql connector

在我正在处理的XMLRPC服务器中(基于xmlrpc-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连接器线程安全吗?

3 个答案:

答案 0 :(得分:4)

在谷歌搜索解决方案时,我遇到了sql::Driver::threadInit()sql::Driver::threadEnd()的提及。但是,正如我在C ++连接器的1.0.5版本上所做的那样,我无法使用这些功能。在获取驱动程序实例后添加driver->threadInit();并在函数结束时添加driver->threadEnd();,此问题已得到解决。

以下是MySQL's 1.1.0 change history中的这个线程init和end功能的提及:

  

添加了Driver :: threadInit()和Driver :: threadEnd()方法。一切   线程客户端的线程必须调用Driver :: threadInit()   在使用Connector / C ++执行任何其他操作之前启动该线程   并且每个线程在完成时必须调用Driver :: threadEnd()。您   可以找到一个示例来演示examples / pthreads.cpp中的用法。它   强烈建议不要在线程之间共享连接。它是   理论上可行,如果你设置某些(未记录的)互斥锁,但是   它根本不受支持。每个线程使用一个连接。没有   两个线程同时使用相同的连接。请检查   关于MySQL手册中的线程的C API说明。 Connector / C ++包装   C API。 (劳伦,安德烈,乌尔夫)

TL; DR:如果您遇到此问题,请确保您的C ++ MySQL Connector版本是&gt; = 1.1.0并使用sql::Driver::threadInit()sql::Driver::threadEnd()方法围绕您的连接代码。

答案 1 :(得分:0)

两个想法:

  1. libmysql不完全thread safe
  2. 您的代码的结构方式如果发生异常,您将泄漏内存。您可以通过在 try / catch 之外声明变量并使用 finally (或本地等效项)来确保正确清理或使用智能指针< / strong>(如果有的话)。
  3. 由于您没有显示任何主叫或周围代码,因此很难说出实际发生了什么。你应该完成后检查线程的退出代码吗?你可以在调试器中附加它以查看它正在做什么而不是关闭吗?

答案 2 :(得分: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服务器添加进程!!!

FURTHERMORE:阅读官方手册https://docs.oracle.com/cd/E17952_01/connector-cpp-en/connector-cpp-en.pdf

替代方法:如果您无法正确关闭程序/功能端的连接和查询(从而将进程添加到MySQL服务器),请考虑以下两个选项:

1 /将所有MySQL超时参数设置为10秒。或更少(例如); 2 /编写SHOW PROCESSLIST的脚本并删除SLEEP中的进程太长时间。

干杯。