多线程使用unixODBC时,必须的配置(threading)(转)

 unixODBC是linux在平台上实现的ODBC驱动管理器(Driver Manager),当应用程序调用C函数SQLDriverConnect时,会依据参数以及ODBC的配置文件内容去调用具体的ODBC驱动程序动态库。比如MaxDB的驱动libsqlod.so和MySQL的MyODBC驱动libmyodbc3.so等。 

  在linux下,ODBC的配置文件有如下位置: 

  ~/.odbc.ini 

  /etc/odbc.ini 或者 /usr/local/etc/odbc.ini 

  /etc/odbcinst.ini 或者 /usr/local/etc/odbcinst.ini 

  通常以rpm安装的unixODBC,是/etc下的配置文件起作用,如果是编译后安装的,是/usr/local/etc下的配置文件起作用;~/.odbc.ini是对该 

  用户的进程起作用,比如redhat系列的操作系统,以用户normal运行的进程,则会去找/home/normal/.odbc.ini。 

  配置文件中的大多数参数,在很多网站上都有说明,比如微软的网站、unixODBC的网站、MySQL的网站等等,这里就不多说了。本文只介绍一个unixODBC特有的,又与多线程有很大关系的参数----threading。从目前的经验来看,只要使用多线程来调用ODBC,用MySQL和MaxDB时是必须要设置的,通常设为0. 

  threading共有四个可取的值,从0到3.如果不设置此值的话,unixODBC会使用一个缺省值,比如我遇到的情况,缺省值是3。 

  在取1到3时,unixODBC会在调用ODBC函数时,加不同级别的锁,锁的粒度越粗,则各线程之间就越容易由于锁的互斥而进行等待。这种做法的初衷是考虑到具体的驱动可能不是线程安全的。 

  但是MaxDB的ODBC驱动,和MyODBC驱动,在各个线程不共享数据库连接的情况下,都是线程安全的。 

  并且从这一段时间的实践,如果不设置threading(即使用缺省值),一方面会导致速度很慢,另一方面会由于unixODBC加的锁与数据库端的锁共同作用而造成死锁。 

  比如有两个线程,分别称为A和B,它们都通过ODBC访问同一个数据库。 

  首先,A由于某个数据库操作,获得了数据库的某个锁,不妨称为DBLock; 

  然后,B调用某个ODBC函数,由unixODBC依据threading的值,通过pthread_mutex_lock获得一个锁,不妨称为UNLock; 

  之后,B恰巧由于某个操作,请求A获得的DBLock,此时在数据库一端就被锁住了,等待DBLock的释放,则B一直等待数据库的回应; 

  之后,A又要调用某个ODBC函数,而恰巧又要请求B已经获得的UNLock,这样,线程A就阻塞了,等待UNLock的释放。 

  也就是说,A在等B,B又在等A,客户端的锁与服务器端的锁共同作用,造成死锁。线程并未报错或者退出,但也不干活。 

  以目前的经验,在linux上使用ODBC连接MaxDB和MySQL,要在odbcinst.ini中加一句: 

  threading = 0 

  至于它的具体含义,我在网上查了挺久都没看到对它的详细解释,有点印象,以前好像在哪里见过的,但现在怎么也找不到了。最后在unixODBC的网站上发现这样一段话: 

  Since 1.6 if the driver manager was built with thread support you may add another entry to each driver entry. For example 

  [PostgreSQL] 

  Description = PostgreSQL driver for Linux & Win32 

  Driver = /usr/local/lib/libodbcpsql.so 

  Setup = /usr/local/lib/libodbcpsqlS.so 

  Threading = 2 

  This entry alters the default thread serialization level. More details can be found in the file DriverManager/__handles.c in 

  the source tree. 

  看unixODBC相关源码,首先是与threading取值相关的宏: 

  #if defined ( HAVE_LIBPTHREAD ) || defined ( HAVE_LIBTHREAD ) || defined ( HAVE_LIBPTH ) 

  #define TS_LEVEL0 0 /* no implicit protection, only for */ 

  /* dm internal structures */ 

  #define TS_LEVEL1 1 /* protection on a statement level */ 

  #define TS_LEVEL2 2 /* protection on a connection level */ 

  #define TS_LEVEL3 3 /* protection on a environment level */ 

  #endif 

  共有四个值,表示在什么级别上加锁。在unixODBC有这样一个函数,几乎在调每个ODBC函数的时候,都会调这个函数: 

  void thread_protect( int type, void *handle ) 

  { 

  DMHENV environment; 

  DMHDBC connection; 

  DMHSTMT statement; 

  DMHDESC descriptor; 

  switch( type ) 

  { 

  case SQL_HANDLE_ENV: 

  mutex_entry( &mutex_env ); 

  break; 

  case SQL_HANDLE_DBC: 

  connection = handle; 

  if ( connection -> protection_level == TS_LEVEL3 ) 

  { 

  mutex_entry( &mutex_env ); 

  } 

  else if ( connection -> protection_level == TS_LEVEL2 || 

  connection -> protection_level == TS_LEVEL1 ) 

  { 

  mutex_entry( &connection -> mutex ); 

  } 

  break; 

  case SQL_HANDLE_STMT: 

  statement = handle; 

  if ( statement -> connection -> protection_level == TS_LEVEL3 ) 

  { 

  mutex_entry( &mutex_env ); 

  } 

  else if ( statement -> connection -> protection_level == TS_LEVEL2 ) 

  { 

  mutex_entry( &statement -> connection -> mutex ); 

  } 

  else if ( statement -> connection -> protection_level == TS_LEVEL1 )



  mutex_entry( &statement -> mutex ); 

  } 

  break; 

  case SQL_HANDLE_DESC: 

  descriptor = handle; 

  if ( descriptor -> connection -> protection_level == TS_LEVEL3 ) 

  { 

  mutex_entry( &mutex_env ); 

  } 

  if ( descriptor -> connection -> protection_level == TS_LEVEL2 ) 

  { 

  mutex_entry( &descriptor -> connection -> mutex ); 

  } 

  if ( descriptor -> connection -> protection_level == TS_LEVEL1 ) 

  { 

  mutex_entry( &descriptor -> mutex ); 

  } 

  break; 

  } 

  } 

  这样基本上就有些概念了。 

  结论是,使用unixODBC时,一定要在odbcinst.ini中设置threading,通常设为0,避免用它的缺省值(很可能是3)。 


你可能感兴趣的:(多线程使用unixODBC时,必须的配置(threading)(转))