The client library is almost thread-safe. The biggest problem is
        that the subroutines in net.c that read
        from sockets are not interrupt safe. This was done with the
        thought that you might want to have your own alarm that can
        break a long read to a server. If you install interrupt handlers
        for the SIGPIPE interrupt, the socket
        handling should be thread-safe.
      
        To avoid aborting the program when a connection terminates,
        MySQL blocks SIGPIPE on the first call to
        mysql_library_init(),
        mysql_init(), or
        mysql_connect(). If you want to
        use your own SIGPIPE handler, you should
        first call mysql_library_init()
        and then install your handler.
      
Before MySQL 4.0, binary client libraries that we provided other than those for Windows were not normally compiled with the thread-safe option. Current binary distributions should have both a normal and a thread-safe client library.
        To create a threaded client where you can interrupt the client
        from other threads and set timeouts when talking with the MySQL
        server, you should use the net_serv.o code
        that the server uses and the -lmysys,
        -lmystrings, and -ldbug
        libraries.
      
        If you don't need interrupts or timeouts, you can just compile a
        thread-safe client library (mysqlclient_r)
        and use it. In this case, you don't have to worry about the
        net_serv.o object file or the other MySQL
        libraries.
      
        When using a threaded client and you want to use timeouts and
        interrupts, you can make great use of the routines in the
        thr_alarm.c file. If you are using routines
        from the mysys library, the only thing you
        must remember is to call
        my_init() first! See
        Section 20.10.8, “C API Threaded Function Descriptions”.
      
        In all cases, be sure to initialize the client library by
        calling mysql_library_init()
        before calling any other MySQL functions. When you are done with
        the library, call
        mysql_library_end().
      
        mysql_real_connect() is not
        thread-safe by default. The following notes describe how to
        compile a thread-safe client library and use it in a thread-safe
        manner. (The notes below for
        mysql_real_connect() also apply
        to the older mysql_connect()
        routine as well, although
        mysql_connect() is deprecated
        and should no longer be used.)
      
        To make mysql_real_connect()
        thread-safe, you must configure your MySQL distribution with
        this command:
      
shell> ./configure --enable-thread-safe-client
        Then recompile the distribution to create a thread-safe client
        library, libmysqlclient_r. (Assuming that
        your operating system has a thread-safe
        gethostbyname_r() function.) This library is
        thread-safe per connection. You can let two threads share the
        same connection with the following caveats:
      
            Two threads can't send a query to the MySQL server at the
            same time on the same connection. In particular, you have to
            ensure that between calls to
            mysql_query() and
            mysql_store_result() no
            other thread is using the same connection.
          
            Many threads can access different result sets that are
            retrieved with
            mysql_store_result().
          
            If you use
            mysql_use_result(), you must
            ensure that no other thread is using the same connection
            until the result set is closed. However, it really is best
            for threaded clients that share the same connection to use
            mysql_store_result().
          
            If you want to use multiple threads on the same connection,
            you must have a mutex lock around your pair of
            mysql_query() and
            mysql_store_result() calls.
            Once mysql_store_result() is
            ready, the lock can be released and other threads may query
            the same connection.
          
            If you use POSIX threads, you can use
            pthread_mutex_lock() and
            pthread_mutex_unlock() to establish and
            release a mutex lock.
          
You need to know the following if you have a thread that is calling MySQL functions which did not create the connection to the MySQL database:
        When you call mysql_init(),
        MySQL creates a thread-specific variable for the thread that is
        used by the debug library (among other things). If you call a
        MySQL function before the thread has called
        mysql_init(), the thread does
        not have the necessary thread-specific variables in place and
        you are likely to end up with a core dump sooner or later. To
        get things to work smoothly you must do the following:
      
            Call mysql_library_init()
            before any other MySQL functions. It is not thread-safe, so
            call it before threads are created, or protect the call with
            a mutex.
          
            Arrange for
            mysql_thread_init() to be
            called early in the thread handler before calling any MySQL
            function. If you call
            mysql_init(), they will call
            mysql_thread_init() for you.
          
            In the thread, call
            mysql_thread_end() before
            calling pthread_exit(). This frees the
            memory used by MySQL thread-specific variables.
          
        The preceding notes regarding
        mysql_init() also apply to
        mysql_connect(), which calls
        mysql_init().
      
        If “undefined symbol” errors occur when linking
        your client with libmysqlclient_r, in most
        cases this is because you haven't included the thread libraries
        on the link/compile command.
      


User Comments
Sharing one connection between threads doesn't make much sense anyway. *Passing* it, yes, for instance by way of a connection pool. But *sharing*? MySQL connections are not resource intense: just open a second one.
In fact, I found plenty of connections of mysql client doesn't make the database faster at all. Try to use as less as u can in your program
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <mysql.h>
// Just a small exmaple of multithreading, MUST link with -lpthreads -lmysqlclient_r
// Note: Optimal # of threads and connections pool is the # of CPUs BUT,
// that depends a lot on how fast you expect the answer to your queries
#define QPERTHR 500
#define THREADS 2
#define CONPOOL (THREADS)
typedef struct db_donfig {
char host[16];
char user[16];
char pass[16];
char name[16];
unsigned int port;
char *socket;
} db_config;
typedef struct db_mutex {
MYSQL *db;
pthread_mutex_t lock;
} db_mutex;
db_mutex dbm[CONPOOL];
void *db_pthread(void *arg);
static void db_die(char *fmt, ...);
MYSQL *db_connect(MYSQL *db, db_config *dbc);
void db_disconnect(MYSQL *db);
long db_query(MYSQL *db, pthread_mutex_t *lock, const char *query);
int main(int argc, char **argv) {
int i;
pthread_t pthread[THREADS];
db_config dbc;
strcpy(dbc.host,"");
strcpy(dbc.user,"");
strcpy(dbc.pass,"");
strcpy(dbc.name,"");
dbc.port = 3306;
dbc.socket = NULL;
if (!mysql_thread_safe())
fprintf(stderr, "Thread Safe OFF\n");
else
fprintf(stderr, "Thread Safe ON\n");
fprintf(stdout, "DB Connections: %d, Threads: %d, Queries per Thread: %d, Total Queries: %d\n",\
CONPOOL, THREADS, QPERTHR, THREADS * QPERTHR);
// pre initialize connections and locks
for (i = 0; i < CONPOOL; ++i) {
dbm[i].db = db_connect(dbm[i].db, &dbc);
pthread_mutex_init(&dbm[i].lock, NULL);
}
// pthread_setconcurrency(THREADS);
// fire up the threads
for (i = 0; i < THREADS; ++i)
pthread_create(&pthread[i], NULL, db_pthread, (void *)i);
// wait for threads to finish
for (i = 0; i < THREADS; ++i)
pthread_join(pthread[i], NULL);
for (i = 0; i < CONPOOL; ++i) {
pthread_mutex_destroy(&dbm[i].lock);
db_disconnect(dbm[i].db);
}
exit(EXIT_SUCCESS);
}
void *db_pthread(void *arg) {
int i = (int) arg, j, cancelstate;
// Always a good idea to disable thread cancel state or
// unexpected crashes may occur in case of database failures
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&cancelstate);
if ((mysql_thread_init() != 0))
db_die("mysql_thread_init failed: %s", mysql_error(dbm[i].db));
for (j = 0; j < QPERTHR; ++j)
db_query(dbm[i].db, &(dbm[i].lock), "show status");
mysql_thread_end();
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&cancelstate);
pthread_exit((void *)0);
}
static void db_die(char *fmt, ...) {
int i;
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
(void)putc('\n', stderr);
for (i = 0; i < CONPOOL; ++i) {
pthread_mutex_destroy(&dbm[i].lock);
db_disconnect(dbm[i].db);
}
exit(EXIT_FAILURE);
}
MYSQL *db_connect(MYSQL *db, db_config *dbc) {
if (!(db = mysql_init(db)))
db_die("mysql_init failed: %s", mysql_error(db));
else {
if (!mysql_real_connect(db, dbc->host, dbc->user, dbc->pass, dbc->name, dbc->port, dbc->socket, 0))
db_die("mysql_real_connect failed: %s", mysql_error(db));
}
return (db);
}
void db_disconnect(MYSQL *db) {
if (db)
mysql_close(db);
}
long db_query(MYSQL *db, pthread_mutex_t *lock, const char *query) {
long ret;
// lock must be called before mysql_query
pthread_mutex_lock(lock);
ret = mysql_query(db, query);
// if query failed, exit with db error
if (ret != 0) {
// Get rid of the lock first
pthread_mutex_unlock(lock);
db_die("mysql_query failed: %s", mysql_error(db));
}
// if query succeeded
else {
MYSQL_RES *res;
res = mysql_store_result(db);
// Get rid of the lock ASAP, only safe after mysql_store_result
pthread_mutex_unlock(lock);
// if there are rows
if (res) {
MYSQL_ROW row, end_row;
unsigned int num_fields;
num_fields = mysql_num_fields(res);
// count total rows * fields and return the value, if SELECT
while ( (row = mysql_fetch_row(res)) )
for (end_row = row + num_fields; row < end_row; ++row)
++ret;
mysql_free_result(res);
}
// if there are no rows, should there be any ?
else {
// if query was not a SELECT, return with affected rows
if (mysql_field_count(db) == 0)
ret = mysql_affected_rows(db);
// there should be data, exit with db error
else
db_die("mysql_store_result failed: %s", mysql_error(db));
}
}
return (ret);
}
Thank you Lefteris; the example helps a lot!
At the end of db_thread, shouldn't you call:
pthread_setcancelstate(cancelstate,0);
to restore it, instead of
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &cancelstate);
Thanks
npj
Also,
You need to initialize the db_mutex::db fields to null. It's likely the compiler would do this automatically since your dbm array is statically allocated, but this practice could lead to confusing bugs in an example.
If you need multithreaded client and standard
MYSQL_OPT_CONNECT_TIMEOUT / MYSQL_OPT_READ_TIMEOUT / MYSQL_OPT_WRITE_TIMEOUT
settings do not work for you, you really need to go the hard way.
Regretfully, this chapter is pretty vague about this point.
After spending ~3 working days:
1. download and compile mysql sources
#define add --with-debug if you need mysql calls logging and debugging
./configure --enable-thread-safe-client --with-unix-socket-path=/var/lib/mysql/mysql.sock
make
2. link your application against all these
net_serv.o -lmysys -lmystrings -ldbug -lmysqlclient_r
3. in all threads do not forget about mysql_thread_init/mysql_thread_end, I suggest using the MySQLThreadData C++ sugar (below).
4. at start of your application do this magic
int main(int argc, char *argv[]) {
// http://dev.mysql.com/doc/refman/4.1/en/threaded-clients.html
// plus they don't tell you about init_thr_alarm and THR_SERVER_ALARM handling
if (mysql_server_init(0, NULL, NULL)) {
mmErr("mysql_server_init failed\n");
exit(-1);
}
MySQLThreadData sqlThreadData; // sequence like in thr_alarm.c:signal_hand
init_thr_alarm(1000); // this is max number of threads in your app todo:find out what happends if we exceed that?
{
// we are not using mysql signal thread, so install and unblock handler
my_sigset(THR_SERVER_ALARM, process_alarm);
sigset_t s; sigemptyset(&s);
sigaddset(&s, THR_SERVER_ALARM);
pthread_sigmask(SIG_UNBLOCK, &s, NULL);
}
class MySQLThreadData {
public:
MySQLThreadData() { mysql_thread_init(); }
~MySQLThreadData() { mysql_thread_end(); }
};
5. if you need deeper understanding about internals of mysql client, use --with-debug when configuring mysql, and use
MYSQL_DEBUG=d:t command-to-start-your-application
to instruct mysql library to print debug info to stderr.
more on debugging here:
http://dev.mysql.com/doc/refman/5.0/en/the-dbug-package.html
forgot to mention that you also need tricky includes, to fetch all appropriate internals:
// be extra careful, this should be mysql config.h, not your local!
#undef PACKAGE_VERSION
#include <config.h>
#include <my_global.h>
#include <my_dbug.h>
#include <my_pthread.h>
#include <thr_alarm.h>
#include <mysql.h>
Yet also forgot to mention fake mysqld.cpp that is also needed .
#include <mysql.h>
uint test_flags;
ulong bytes_sent;
ulong bytes_received;
ulong net_big_packet_count;
void query_cache_insert(NET *, const char *, ulong ) {}
Add your own comment.