Odoo能自己写多线程程序吗? Why not ?
简介
从Odoo自身的机制上来讲,它的每一个连接都是一个单线程作业的过程。
Odoo在连接池(ConnectionPool)中加入了线程锁threading.Lock(),监测DB连接数与线程建立连接数量。
当线程池中的DB连接数大于DB连接阈值,就会报错线程池满了,线程池满后新的客户端访问会被拒绝,后台任务或者新的线程任务也会失败(可能会导致程序不执行,更严重的会导致数据丢失)。
虽然框架有各种各样的限制,那我们能否在自己的代码中实现多线程代码,用以提高程序执行效率呢?当然可以 看例子:
实例代码
import odoo
import threading
class ...
@api.model
def main_func_ytc(self):
userids = [1,2,3]
# 最大占用10个线程 防止线程池占满(DB连接数默认64,除过系统基本占用10-20个,我们自定义5-10个线程基本都在范围内)
max_connections = 10
pool_sema = threading.BoundedSemaphore(max_connections)
print('这是主线程:{}'.format(threading.current_thread().name))
thread_list=[]
for userid in userids:
pool_sema.acquire() # 线程加锁
# pool_sema 用于控制程序执行线程总量
m = threading.Thread(target=self.sub_func, args=(pool_sema, userid))
thread_list.append(m)
for m in thread_list:
# m.setDaemon(True) # 目前并不需要使用守护线程
m.start()
#for m in thread_list:
# m.join()
@api.model
def sub_func_ytc(self, pool_sema, userid):
with api.Environment.manage():
# 新线程中不能用老的游标,因为主线程不是守护线程,没有join的话,可能游标就关闭了,
# 所以要新建连接和游标,这里的self.pool.cursor() 其实是先创建连接 _cnx,再创建游标 _obj, 切记返回值并不是游标,而是Cursor对象的实例。
new_cr = self.pool.cursor()
self = self.with_env(self.env(cr=new_cr))
# START 方法功能代码内容(ORM/SQL均可)#
self.env['xxx'].search([])
sql = "insert into XXX() values ();"
self._cr.execute(sql)
# END 方法功能代码内容 #
# 提交事务 self._cr._cnx.commit() 也行
# 反正这里的commit是conn.commit(),或者说是_cnx.commit()
self._cr.commit()
# 关闭游标(基操)
new_cr.close()
# 关闭连接 _cnx = conn :
# 如果不手动关闭连接 你可以查看SQL连接数 系统不会默认关 时间长了会导致前面简介中提到的DB连接数超出
# select * from pg_stat_activity order by pid desc;
new_cr._cnx.close()
pool_sema.release() # 线程解锁
说明
基本的描述都在代码注释中说明了,你只需要针对性的调整:
- 函数名称与参数参数
- 线程数 max_connections
- 方法功能代码内容
结论
我在多线程下的项目开发中所能体会到的是:
需要调用108次企业微信接口的数据获取,正常流程需要38s
使用10线程跑批的方式,仅需要2s
本文章基于Odoo12测试, 如有问题欢迎交流。