You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Odoo 14多线程使用基础与常见问题咨询

Odoo 14 多线程操作指南(附批量价格同步场景实践)

一、新线程中操作记录(读写删)

Odoo的数据库游标cr和环境env都是线程不安全的,绝对不能在多线程间共享。每个线程必须独立创建数据库连接、游标和环境:

  • 核心步骤:
    1. 导入依赖模块:import threading、from odoo.sql_db import db_connect、from odoo.api import Environment
    2. 在线程函数内创建独立的数据库连接与游标
    3. 构建线程专属的环境env(需传入cr、用户ID、上下文)
  • 示例代码:
import threading
from odoo.sql_db import db_connect
from odoo.api import Environment
from odoo import SUPERUSER_ID

def sync_supplier_prices(supplier_id):
    # 创建独立数据库连接
    with db_connect('your_db_name') as conn:
        with conn.cursor() as cr:
            # 构建线程专属环境
            env = Environment(cr, SUPERUSER_ID, {})
            try:
                # 读操作:获取该供应商的待同步产品
                products = env['product.product'].search([('supplier_id', '=', supplier_id)])
                # 写操作:批量更新价格(推荐批量操作提升性能)
                update_vals = [(product.id, {'list_price': get_supplier_price(product.default_code)}) for product in products]
                env['product.product'].browse([pid for pid, _ in update_vals]).write({
                    'list_price': {pid: val['list_price'] for pid, val in update_vals}
                })
                # 创建操作:新增产品(按需使用)
                # env['product.product'].create({'name': 'New Supplier Product', 'list_price': 150})
                # 删除操作:移除无效产品(按需使用)
                # env['product.product'].browse(invalid_product_ids).unlink()
                # 提交事务
                cr.commit()
            except Exception as e:
                # 出错回滚当前线程的所有操作
                cr.rollback()
                # 记录错误日志
                env['ir.logging'].create({
                    'name': 'Supplier Price Sync Error',
                    'type': 'server',
                    'message': f"Supplier {supplier_id}: {str(e)}",
                    'level': 'error'
                })
                cr.commit()

# CRON任务中启动多线程
def cron_sync_all_prices():
    suppliers = env['res.partner'].search([('is_supplier', '=', True)])
    threads = []
    for supplier in suppliers:
        t = threading.Thread(target=sync_supplier_prices, args=(supplier.id,))
        threads.append(t)
        t.start()
    # 直接返回避免阻塞CRON,线程在后台运行

二、Commit与Rollback的时机

  1. Commit时机:
    • 完成一组独立的操作后提交,比如处理完一个供应商的所有产品后执行cr.commit()
    • 大批次数据建议拆分提交(比如2000个产品分4批,每500个提交一次),防止事务过大导致锁表或性能下降
    • 线程内操作必须手动提交,Odoo的自动提交机制仅针对主线程的请求上下文
  2. Rollback时机:
    • 当某一组操作出现异常时回滚,避免部分数据写入导致数据不一致
    • 比如单个供应商同步出错,回滚该供应商的所有操作,不影响其他线程的执行
  3. 注意:线程内的事务完全独立,不会干扰主线程或其他线程的事务

三、cr.savepoint() 是什么?

cr.savepoint()是事务内的保存点,用于在事务中标记一个临时节点,后续如果出现错误,可以只回滚到这个节点,而不是整个事务。

  • 适用场景:批量处理数据时,某一条记录出错,跳过该记录继续处理其他记录,无需回滚全部操作
  • 示例代码:
# 批量处理产品时使用保存点
cr.savepoint('batch_sync')
try:
    for product in products:
        try:
            price = get_supplier_price(product.default_code)
            product.write({'list_price': price})
        except Exception as e:
            # 记录单产品错误,不中断批量操作
            env['ir.logging'].create({
                'name': 'Single Product Sync Error',
                'message': f"Product {product.id}: {str(e)}",
                'level': 'warning'
            })
    cr.commit()
except Exception as e:
    # 批量操作整体出错,回滚到保存点
    cr.rollback('batch_sync')
    cr.commit()

四、你的批量价格同步场景注意事项

  1. CRON任务不阻塞:CRON函数启动线程后直接返回,不要调用thread.join(),让Odoo主线程继续处理其他任务,线程在后台执行
  2. 线程数量控制:5个供应商对应5个线程即可,避免线程过多耗尽数据库连接(可在Odoo配置文件调整db_maxconn参数扩容连接数)
  3. 异常与日志:每个线程必须捕获异常并记录详细日志,方便排查同步失败的产品或供应商
  4. 资源隔离:绝对不要在多线程间共享cr、env或记录集,每个线程必须独立创建
  5. 性能优化:
    • 批量查询产品,避免循环内重复发起数据库请求
    • 用批量write替代循环单条写入,减少数据库交互次数
    • 对HTTP请求做超时控制,避免单个供应商请求阻塞线程

常见陷阱

  • 共享游标/环境:直接复用主线程的cr或env会导致数据混乱、死锁甚至程序崩溃
  • 未手动提交/回滚:线程内操作不会自动提交,未提交的数据会永久丢失,未回滚的事务会长期占用数据库锁
  • 线程过载:线程数量超过数据库连接上限,导致Odoo主进程无法处理正常请求
  • 静默异常:线程内未捕获异常会导致线程直接退出,无法及时发现同步失败问题

内容的提问来源于stack exchange,提问作者xixo222

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 05:45:42