[总结]UserWarning: MongoClient opened before fork.相关文档与理解

错误的基本理解与解决办法

/usr/local/lib/python3.5/dist-packages/pymongo/topology.py:75: UserWarning: MongoClient opened before fork. Create MongoClient with connect=False, or create client after forking. See PyMongo's documentation for details: http://api.mongodb.org/python/current/faq.html#using-pymongo-with-multiprocessing>
  "MongoClient opened before fork. Create MongoClient "

我们可以看到警告的大致意思为:MongoClient在fork前连接被打开。请在实例化MongoClient时,传入connect=False,或者在fork后实例化MongoClient。
(connect默认为True,代表MongoClient会在实例化时立即链接mongodb,但当其为False时,实例化的MongoClient会在第一次进行操作时进行连接。)

文档中对于在使用Multiprocessing时使用PyMongo的简单实例

文档原文
在Unix(或类Unix)系统中multiprocessing包使用fork()函数创建新进程。在使用MongoClient实例与fork()时必须小心。千万不要使得子进程的MongoClient实例直接由父进程copy而来。尤其注意,一定要在父进程与各个子进程中创建其各自的MongoClient实例。例如:

# 各个进程创建自己的MongoClient实例
def func():
    db = pymongo.MongoClient().mydb
    # Do something with db.

proc = multiprocessing.Process(target=func)
proc.start()

千万不要这样玩↓↓↓!!!

client = pymongo.MongoClient()

# 每个子进程试图去copy一个在父进程中的全局的MongoClient.千万别这么玩!
def func():
  db = client.mydb
  # Do something with db.

proc = multiprocessing.Process(target=func)
proc.start()

从父进程copy一个MongoClient实例有很大的可能会因为与fork()固有的不兼容性在子进程中造成死锁(好长一句话…)
当有可能发生死锁事故时,PyMongo会尝试发出警告。

文档中关于PyMongo是否进程安全与线程安全的相关说明

PyMongo线程安全

文档
如题,PyMongo是线程安全的,因为它使用了内置的连接池为线程服务。

为什么使用连接池就线程安全呢?

文档
这里我大概说明下文档前两段内容:
每个MongoClient实例都有一个內建的连接池,连接池会按需打开套接字,而每个套接字间没有线程关联。
每个连接池有个连接数上限(maxPoolSize 默认为100),当所有链接都正在使用时,新的请求将等待知道有连接可用。(也就是大家排队用,不争不抢)

PyMongo并不fork-safe

文档
MongoClient产生大量的线程执行后台工作,例如监控连接服务。这些线程共享本不fork-save的锁实例所保护的状态(也就是锁的状态)。因此driver(应该是指的PyMongo比较底层的代码吧)也会像其他使用锁(与一般的互斥器)的多线程代码一样受到限制。限制其一是锁会在fork()后失效。通过fork(),所有的锁会被从父进程中保持原有状态copy到子进程:如果是上锁状态,那么copy道德锁也会是上锁状态。被fork()创建的子进程只有一个线程,所以,任何从其他(父进程中的)线程得到锁在子进程中将无法释放。之后子进程尝试获取这些锁时,将会发生死锁。

其他

  1. 默认python内部对象是thread-safe的

拓展阅读