3

在我正在处理的 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 连接器线程安全吗?

4

3 回答 3

4

在谷歌搜索解决方案时,我遇到了sql::Driver::threadInit()和的提及sql::Driver::threadEnd()。但是,由于我使用的是 C++ 连接器 1.0.5 版,因此我无法使用这些功能。driver->threadInit();在获取驱动程序实例后并driver->threadEnd();在我的函数结束时添加一个,这个问题就解决了。

以下是MySQL 1.1.0 更改历史中提到的这个线程的初始化和结束功能:

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

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

于 2013-04-23T13:05:39.810 回答
0

两个想法:

  1. libmysql 不是完全线程安全的。
  2. 如果发生异常,您的代码的结构方式将泄漏内存。通过在try/catch之外声明变量并使用finally(或本地等效项)来确保正确清理或使用智能指针(如果可用),您可能会得到更好的服务。

由于您没有显示任何调用或周围代码,因此很难判断实际发生了什么。当它应该完成时,您是否检查线程的退出代码?您可以将它附加到调试器中以查看它在做什么而不是关闭吗?

于 2013-04-23T12:05:52.240 回答
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 脚本并删除处于 SLEEP 状态太久的进程。

干杯。

于 2016-03-28T12:41:55.773 回答