OCCI 线程安全
一、问题背景:
C++程序中使用oracle occi来链接oracle数据库并进行相关操作。整个程序分成了许多个模块,其中有个抽象和封装oracle数据库的模块,并且使用了单例模式,其他模块若想操作oracle数据库,则必须首先获取oracle链接对象,然后通过对象调用相关的方法。整个程序设计思想是能够同时接受大量任务提交,并将这些任务塞到一个任务队列中。有一个单独的线程负责来从队列中取任务,然后写入数据库。所以从设计上来看,最终操作链接对象的只有一个线程,而且任意时刻链接对象只被一个线程所占用,执行一个SQL命令。但是当同时有超多30个任务一起提交时,程序就会出现数据库相关的异常:
image.png例如,ORA-01013:user requested cancel of current operation。
二、解决办法
将创建oracle链接环境的模式由DEFAULT改为THREADED_MUTEXED,即线程安全模式。
image.png三、问题缘由
经过仔细查看代码,发现是在其他地方又使用了连接对象来进行ID相关的查询操作,而链接环境模式DEFAULT并不是线程安全的,所以在多线程环境下,在两个线程中同时使用了同一个链接对象来进行数据库相关操作,在没有锁机制同步的情况下,产生了竞争条件,最终破坏了链接对象内部的状态,产生了异常。
四、相关知识点
image.png对于参数THREADED_MUTEXED,occi 文件介绍如下:
THREADED_MUTEXED is a thread safe mode for creating an Environment
object, mutexed internally by OCCI.
其实就是说,如果你的程序是多个线程可能同时访问数据库的话,请使用多线程安全的环境。
The Programmer must insure that only one OCI call is in process on the environment handle connection at any given time
任意时间片,在同一个连接环境句柄下,只能有一个OCI命令运行
线程安全性就是说OCI中的那些句柄,比如环境句柄、服务句柄等,能不能在多个线程中共享。遗憾的是,结合OCI的文档和论坛上他人的描述,我的出结论是不能共享。多个线程同时访问不会导致程序的崩溃,OCI内部使用互斥体保证句柄的线程安全性,但是不能拿一个句柄同时做两件事。按照OTN上一个人说,在共享句柄上同时做不同的事情会得到一个错误:ORA-03127: no new operations allowed until the active operation ends。