sqlite3_mutex_enter() 访问冲突。为什么?
我在一个Python3/C模块中使用sqlite-amalgamation-3080500。
我的Python模块创建了一些表格,然后通过PyCapsule把sqlite3的句柄返回给Python环境。
所以,在第二个模块中,我尝试使用这个相同的sqlite3句柄来创建更多的表格。但我的程序出错了。我遇到了一个“访问冲突错误”,这个错误是在sqlite3_prepare_v2()调用的sqlite3_mutex_enter()中发生的。
在python.exe中发生第一次异常:0xC0000005:在位置0x00000000执行时发生访问冲突。未处理的异常在python.exe中的0x7531C9F1:0xC0000005:在位置0x00000000执行时发生访问冲突。
这真的线程安全吗?我觉得这样做是可以的。我以前也这样做过,但那时我是在Mac上用XCode。现在我想在MSVC 2013上做同样的事情。
下面是我运行查询的代码:
bool register_run(register_db_t *pReg, const char *query)
{
int ret, len;
sqlite3_stmt *stmt;
const char *err;
stmt = NULL;
len = (int)strlen(query);
ret = sqlite3_prepare_v2(pReg->pDb, query, len, &stmt, NULL);
if (ret != SQLITE_OK) {
err = sqlite3_errmsg(pReg->pDb);
fprintf(stderr, "sqlite3_prepare_v2 error: %s\n%s\n",
err, query);
return false;
}
ret = register_run_stmt(pReg, query, stmt);
sqlite3_finalize(stmt);
return ret;
}
这是我如何导出句柄以便在我的第二个C模块中使用:
// Register's getattro
static PyObject* Register_getattro(RegisterObject *self, PyObject *name)
{
// ...
} else if (PyUnicode_CompareWithASCIIString(name, "handle") == 0) {
register_db_t *handle = self->db;
return PyCapsule_New(handle, NULL, NULL);
}
return PyObject_GenericGetAttr((PyObject *)self, name);
}
这是我在Python中连接各个部分的代码:
import registermodule, loggermodule
reg = registermodule.Register("mydata.db")
loggermodule.set_register(reg.handle)
这是我在第二个模块中如何使用这个句柄:
static PyObject* loggerm_set_register(PyObject *self, PyObject *args)
{
register_db_t *pReg;
PyObject *capsule;
if (!PyArg_ParseTuple(args, "O:set_register", &capsule)) {
return NULL;
}
if (!PyCapsule_CheckExact(capsule)) {
PyErr_SetString(PyExc_ValueError,
"The object isn't a valid pointer.");
return NULL;
}
pReg = PyCapsule_GetPointer(capsule, NULL);
if (!logger_set_register(pReg)) {
PyErr_SetString(PyExc_SystemError,
"Could not set the pointer as register.");
return NULL;
}
Py_RETURN_NONE;
}
最后,这是导致出错的例程:
bool logger_set_register(register_db_t *pReg)
{
char *query = "CREATE TABLE IF NOT EXISTS tab_logger ("
"date NUMERIC,"
"level TEXT,"
"file TEXT,"
"function TEXT,"
"line INTEGER,"
"message TEXT)";
g_pReg = pReg;
return register_run(g_pReg, query);
}
还有导致一切出错的sqlite3例程:
SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){
if( p ){
sqlite3GlobalConfig.mutex.xMutexEnter(p);
}
}
抱歉提供了这么多代码片段,但我对这个问题毫无头绪。
提前谢谢你们。
2 个回答
我不太确定你的问题是否直接和sqlite3_initialize();有关。因为sqlite3_initialize();至少会在sqlite3_open_v2这个函数执行时自动调用一次。
我建议你关注一下在sqlite3_open_v2中缺少了选项SQLITE_OPEN_FULLMUTEX的问题。建议你总是设置这个选项。使用互斥锁的代价非常低,尤其是在考虑到Python带来的额外开销时。在单线程情况下几乎可以忽略不计,而在多线程情况下几乎是必须的。我甚至不明白为什么这个选项还会存在,应该总是启用它。所以,宁可安全一些,也不要后悔。我不知道使用它有什么真正的坏处。SQLite是“轻量级”的。
顺便说一下,sqlite3Config.mutex为NULL是合法的。这个值应该只被SQLite使用来检查这个条件,想了解我说的意思,可以在sqlite3.c中搜索
sqlite3_mutex_enter(db->mutex);
来看看。
我不知道为什么,在Windows上,Python的C模块之间的全局变量不一样。在Mac OS上就没有这个问题,尽管我之前有过这样的经验。
在Windows上,Python模块是DLL文件,所以它们之间不共享同一个全局变量堆栈。
我发现sqlite3Config.mutex在我的第二个Python C模块中是NULL,这导致了访问违规错误。但sqlite3Config.mutex是一个全局变量,这个东西应该由之前的模块来初始化。
现在,知道了这一点,我通过调用这个函数解决了问题:
sqlite3_initialize();
现在一切都正常运作了!