1

저는 파이썬 (2.7)과 pymongo (3.3)로 작업하고 있으며 작업을 비동기 적으로 실행하려면 하위 프로세스를 생성해야합니다. 불행히도 pymongo는 here (그리고 자식 프로세스를 생성하기 전에 db와 상호 작용해야 함)에 설명 된 것처럼 fork-safe하지 않습니다. 포크없이 파이썬에서 프로세스 생성하기

나는 subprocess.Popen ( True로 설정 shell으로 다음 False) 및 multiprocessing.Process 사용하여 실험을 달렸다. 내가 말할 수있는 한, 부모 프로세스가 자식 프로세스를 생성하도록 지시하지만, multiprocessing.Process만이 pymongo로 하여금 fork 된 프로세스를 탐지했다는 경고를 출력하게한다.

나는이 일을하는 비법을 궁금해합니다. 아마도 os.system이 나를 대신 할 것 같지만 subprocessos.system의 의도 된 대체품으로 설명되어 있으므로 뭔가 빠져 있는지 궁금합니다.

답변

4

나는 당신이 오해한다고 생각합니다. PyMongo의 문서에서 단일 MongoClient가 포크 - 세이프가 아니라는 경고를하기 때문에 PyMongo가 전체 프로그램이 하위 프로세스를 만들지 못하게한다는 의미로 해석합니다.

단일 MongoClient는 포크 세이프가 아니므로 포크를 만들기 전에 만들지 말고 동일한 MongoClient 오브젝트를 포킹 후에 사용해야합니다. 프로그램 전반에서 PyMongo를 사용하거나 하나의 MongoClient를 fork 이전에 사용하고 다른 하나를 사용하는 것은 모두 안전합니다.

그런 이유로 subprocess.Popen이 좋습니다 : 포크를 실행 한 다음 exec (자식 프로세스에서 다른 프로그램으로 프로그램을 바꾸려면)를 실행하면 나중에 동일한 MongoClient를 자식에서 사용할 수 없습니다.

PyMongo FAQ 인용하려면 : 멀티 모듈이 포크를 사용하여 프로세스를 생성합니다 유닉스 시스템에서

을(). fork()와 함께 MongoClient 인스턴스를 사용할 때는주의해야합니다. 특히, MongoClient 인스턴스는 부모 프로세스에서 자식 프로세스로 복사되어서는 안됩니다. 대신, 부모 프로세스와 각 자식 프로세스는 자신의 MongoClient 인스턴스를 만들어야합니다. 예를 들어 :

# Each process creates its own instance of MongoClient. 
def func(): 
    db = pymongo.MongoClient().mydb 
    # Do something with db. 

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

이 작업을 수행하지 마십시오 : 부모 프로세스에서 복사 MongoClient의

client = pymongo.MongoClient() 

# Each child process attempts to copy a global MongoClient 
# created in the parent process. Never do this. 
def func(): 
    db = client.mydb 
    # Do something with db. 

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

인스턴스 인해 고유로 자식 프로세스에서 교착 상태의 높은 확률을 가지고 fork(), threads 및 lock 사이의 비 호환성. 이 교착 상태가 발생할 가능성이있는 경우 PyMongo는 경고를 발행하려고 시도합니다.

+0

Aha가 합리적입니다.) 또한 자식 프로세스에서 파일 설명자 (특히 소켓 핸들)를 상속하는 잠재적 인 부작용에 대해 궁금해했지만 Popen의 close_fds 인수 주소도 – nonagon

+0

입니다. PyMongo는 FD_CLOEXEC로 소켓을 생성하므로 close_fds를 전달했는지 여부와 관계없이 해당 설명자가 닫힙니다. –

0

* NIX 환경에서 fork를 사용하지 않고 새 프로세스를 생성 할 수있는 방법은 절대적으로 없습니다. 사실 시스템 내부에서 실행되는 모든 프로세스는 init 프로세스의 포크입니다. 안전한 pymongo 핸들을 포크 사이에 사용하는 것이 안전하지 않다는 걸 알고 있습니다. 프로세스 대신 스레드를 생성하거나 자식 포크에서 pymongo에 다른 핸들을 열 수 있습니다.

2

파이썬 3.4 이상으로 이동할 수 있다면 pymongo을 사용하기 전에 multiprocessing start method'forkserver'으로 설정할 수 있습니다. 이는 포크 서버 프로세스를 즉시 중단시키고 이후 모든 사용은 multiprocessing이 기본 프로세스가 아닌 포크 서버를 포크합니다. 따라서 일단 포크 서버가 설정되면 주 프로세스는 pymongo을 사용할 수 있습니다. 포크 서버는이를 사용하지 않으므로 토큰을 발행하는 데 문제가 없습니다.

슬프게도, 시작 메소드는 3.4에서만 추가되었으므로 2.7에 대한 옵션이 아니지만 다른 사람이이 문제를 가지고 있다면 유용 할 수 있습니다.

+0

예, 정확히 내가 바라는 종류입니다. 이제는 pymongo가 fork/exec 후에 작동하지만 새로운 관련이없는 프로세스를 포크하는 것은 하위 프로세스를 생성하는 깨끗한 방법과 같은 느낌을줍니다 (어쩌면 내 Windows 루트가 표시됨). 불행히도 파이썬 2-> 3은 우리의 코드베이스에서 몇 년이 지난 것처럼 느낍니다. ( – nonagon

5

fork하지 않는 것이 fork를 호출 할 수 없다는 것을 의미하지는 않습니다. 단지 자식 프로세스가 상속받은 PyMongo 인스턴스를 사용해서는 안된다는 것을 의미합니다. subprocess.Popen을 사용할 때 새롭게 분기 된 자식은 거의 즉시 exec을 셸 인스턴스 (shell = True) 또는 필요한 실행 파일 (shell = False)로 대체하도록 호출합니다. 따라서 PyMongo의 관점에서 보면 안전합니다.

반대쪽에서 multiprocessing.Process를 호출하면 자식은 실제로 부모의 복사본이며 해당 PyMongo 인스턴스를 유지합니다. 그 맥락에서 PyMongo를 사용하는 것은 안전하지 않으며 경고 메시지가 올바르게 출력되었습니다.