Linux下sqlite3多线程和串⾏模式
sqlite3⽀持三种模式:单线程模式,多线程模式和串⾏模式。
模式可在编译,启动时或运⾏时设置,⼀般来讲,启动时的设置会覆盖编译时的设置,运⾏时则会覆盖启动时或编译时的设置。但是⼀旦单线程模式被设置之后就不能再被覆盖了。
编译时可通过SQLITE_THREADSAFE设置模式。sqlite3标准 发⾏版本默认设置为SQLITE_THREADSAFE=1, 即串⾏模式。
SQLITE_THREADSAFE=2为多线程模式,SQLITE_THREADSAFE=0为单线程模式,当设置为0时,即使初始化或运⾏时设置为其他模式,也会保持单线程模式。
启动时是指在sqlite3在初始化之前调⽤sqlite3_config()函数设置,参数为
SQLITE_CONFIG_SINGLETHREAD,SQLITE_CONFIG_MULTITHREAD或SQLITE_CONFIG_SERIALIZED。
sqlite3初始化指的是调⽤sqlite3_initalize(),该函数会在调⽤sqlite3_open_v2()时调⽤。运⾏时也就是程序第⼀次(仅第⼀次调⽤有效)调⽤该函数创建数据库连接时,通过设置第三个参数为设置不同模式,SQLITE_OPEN_NOMUTEX为多线程模
式,SQLITE_OPEN_FULLMUTEX为串⾏模式。
串⾏模式
串⾏模式⽀持多线程操作,但是必须统⼀使⽤⼀个全局的数据库连接,这⼀点⾮常重要。串⾏模式会打开sqlite3所有的锁,在同⼀时刻保证只有⼀个线程能访问。这⾥可以理解为只有⼀条指向数据库的连接,多个线程的请求将会在该连接上串⾏传输。
多线程模式
1. 多线程模式⽀持线程并发操作,但也有例外,因为在该模式下,sqlite3打开了bCoreMutex锁,关闭了bFullMutex锁,也就禁⽌了
多个线程并发使⽤同⼀个数据库连接和perpared statement, perpared statement可以简单的使⽤信号量进⾏互斥。
2. 使⽤多线程模式,除了需要设置模式外,还必须在执⾏数据库操作前调⽤sqlite3_busy_handler()或sqlite3_busy_timeout() 。这两
个函数会判断sqlite是否处于SQLITE_BUSY状态,是的话将进⾏sleep等待。区别在于sqlite3_busy_h
andler()需要⾃⼰实现等待的处理,⽽sqlite3_busy_timeout()实际上是调⽤了sqlite3_busy_handler()并使⽤⼀个默认的等待处理函数。
3. sqlite3_busy_timeout()通过指定最⼤超时时间进⾏等待。根据sqlite3_busy_timeout()源码,需要注意,如果系统未定义
HAVE_USLEEP或定定义为false,则超时时间必须指定为⼤于1000且是它的整数倍。
⽆论有⽆定义,使⽤sqlite3_busy_timeout()的等待时间都是该函数的算法实现,所以如果需要完全按照⾃⼰的需求决定等待时间,可以使⽤sqlite3_busy_handler(),该函数需要⾃⼰实现等待处理函数,处理函数中即可指定等待时间。
4. sqlite3_busy_timeout() 源码:
static int sqliteDefaultBusyCallback(
void *ptr,              /* Database connection */
int count/* Number of times table has been busy */
)
{
#if SQLITE_OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP)
static const u8 delays[] =
{ 1, 2, 5, 10, 15, 20, 25, 25,  25,  50,  50, 100 };
static const u8 totals[] =
{ 0, 1, 3,  8, 18, 33, 53, 78, 103, 128, 178, 228 };
# define NDELAY (sizeof(delays)/sizeof(delays[0]))
sqlite3 *db = (sqlite3 *)ptr;
int timeout = db->busyTimeout;
int delay, prior;
assert( count>=0 );
if( count < NDELAY ){
delay = delays[count];
prior = totals[count];
}else{
delay = delays[NDELAY-1];
prior = totals[NDELAY-1] + delay*(count-(NDELAY-1));
}
if( prior + delay > timeout ){
delay = timeout - prior;
if( delay<=0 ) return0;
}
sqlite3OsSleep(db->pVfs, delay*1000);
return1;
#else
sqlite3 *db = (sqlite3 *)ptr;
int timeout = ((sqlite3 *)ptr)->busyTimeout;
if( (count+1)*1000 > timeout ){
return0;//1000>timeout,so timeout must bigger than 1000      }
sqlite3OsSleep(db->pVfs, 1000000);//1000ms
return1;
#endif
}
int sqlite3_busy_timeout(sqlite3 *db, int ms){
if( ms>0 ){
db->busyTimeout = ms;
sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db);
}else{
sqlite3_busy_handler(db, 0, 0);
}
return SQLITE_OK;
}
Exmaple
// 判断编译时模式,设置启动时模式为多线程模式
int mode = sqlite3_threadsafe();
if (mode == 0) {
AIO_LOG_W("SQLite database is not compiled to be threadsafe");
return AIO_ERR;
}
int ret = sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
if (ret != SQLITE_OK) {
AIO_LOG_E("setting sqlite thread safe mode to multithread failed: %d", ret);
return AIO_ERR;
}
......
/
/ 设置SQLITE_BUSY 处理,使⽤sqlite3_busy_timeout()
#define SQLITE_EXE(db, sql, callback, ptr, msg) \
{      \
rc = sqlite3_busy_timeout(db, 10 * MSEC_PER_SEC); \
if (rc != SQLITE_OK) {  \
AIO_LOG_E("SQL error: %d, file: %s. func: %s, line: %d.\n", \
rc, __FILE__, __func__, __LINE__);    \
}  \
rc = sqlite3_exec(db, sql, callback, ptr, &msg);  \
if (rc != SQLITE_OK) {  \
AIO_LOG_E("SQL error: %s, file: %s. func: %s, line: %d.\n", \
msg, __FILE__, __func__, __LINE__); \
sqlite3_free(msg);      \
}      \
}
// 设置SQLITE_BUSY 处理,使⽤sqlite3_busy_handler()
static int cb_sql_busy(void *ptr, int count)
{
int timeout = *((int *)ptr);
(void)usleep(timeout);
return1;
}
static int sql_busy_check(sqlite3 *db, int ms)
{
if( ms > 0 )
sqlite3_busy_handler(db, cb_sql_busy, (void*)&ms);
else
sqlite3_busy_handler(db, 0, 0);
return SQLITE_OK;
}
int my_sqlite_exe(...) {
rc = sql_busy_check(db, 100);
......
linux下的sleep函数
rc = sqlite3_exec(db, sql, NULL, NULL, &msg);
......
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。