Django多进程发邮件报错:AttributeError无法序列化本地对象
解决Django多进程发送邮件时的Pickle序列化错误
嘿,我来帮你搞定这个问题!你遇到的AttributeError: Can't pickle local object错误,本质是Windows系统下multiprocessing的工作机制导致的,结合你的代码来看,主要有两个核心问题:
错误根源分析
- 局部函数无法被Pickle序列化:Windows下multiprocessing默认使用
spawn模式启动子进程,这个模式需要把要执行的函数和参数序列化(通过pickle)后传递给子进程。但你把send函数定义在了handle方法内部,属于局部函数,pickle无法识别这种不在模块全局命名空间里的函数,所以报错。 - 跨进程共享邮件连接不安全:你在主进程创建了一个
connection对象然后让所有子进程共用,邮件/数据库连接这类资源是和进程绑定的,跨进程共享会导致连接状态混乱,就算没序列化错误,运行起来也大概率出问题。
修复方案
我给你调整了代码,解决了这两个问题:
from django.core.management.base import BaseCommand from django.core.mail import get_connection, EmailMessage from django.template.loader import get_template from multiprocessing import Pool from sending.models import Subscriber class Command(BaseCommand): @staticmethod def send_single_email(email, subject, body_content, from_addr): # 每个子进程独立创建邮件连接,避免跨进程共享问题 with get_connection() as connection: msg = EmailMessage(subject, body_content, from_addr, [email]) msg.content_subtype = 'html' print(f"Sending email to: {email}") connection.send_messages([msg]) print(f"Successfully sent to {email}") def handle(self, *args, **options): # 提前获取所有收件人、渲染邮件内容(主进程做一次就行) recipients = [sub.email for sub in Subscriber.objects.all()] subject = 'Multiprocessing trying 1' body = get_template('Newsletter20171229.html').render() from_email = 'info@modoodoc.com' # 创建进程池 with Pool(processes=2) as pool: # 用starmap传递多个参数给子进程的函数 pool.starmap( self.send_single_email, [(email, subject, body, from_email) for email in recipients] ) print("All emails have been dispatched. Program exiting.")
关键调整点说明
- 把发送函数移到类的静态方法:
send_single_email是Command类的静态方法,属于模块级可访问的函数,pickle可以正常序列化它。 - 每个子进程独立创建连接:用
with get_connection()上下文管理器,每个子进程自己创建和关闭连接,避免共享资源的冲突。 - 使用starmap传递多参数:因为要给send函数传多个参数(邮件地址、主题、内容、发件人),
starmap可以把元组里的元素作为单独参数传递给函数,比map更灵活。 - 用with管理进程池:自动帮你关闭进程池,不用手动调用
pool.close()或pool.join(),更简洁安全。
如果你不想用静态方法,也可以把send_single_email移到模块最外层(Command类外面),效果是一样的,只要它是模块全局可访问的函数就行。
内容的提问来源于stack exchange,提问作者Sanghoon Lee
相关产品推荐
相关产品推荐

