다중 처리로 인해 Python이 충돌하고 fork ()가 호출 될 때 다른 스레드에서 진행 중일 수있는 오류가 발생합니다.


90

저는 비교적 Python에 익숙하지 않고 for 루프에 대해 다중 처리 모듈을 구현하려고합니다.

일부 Google 비전을 다운로드하고 적용하는 데 필요한 img_urls에 저장된 이미지 URL 배열이 있습니다.

if __name__ == '__main__':

    img_urls = [ALL_MY_Image_URLS]
    runAll(img_urls)
    print("--- %s seconds ---" % (time.time() - start_time)) 

이것은 내 runAll () 메서드입니다.

def runAll(img_urls):
    num_cores = multiprocessing.cpu_count()

    print("Image URLS  {}",len(img_urls))
    if len(img_urls) > 2:
        numberOfImages = 0
    else:
        numberOfImages = 1

    start_timeProcess = time.time()

    pool = multiprocessing.Pool()
    pool.map(annotate,img_urls)
    end_timeProcess = time.time()
    print('\n Time to complete ', end_timeProcess-start_timeProcess)

    print(full_matching_pages)


def annotate(img_path):
    file =  requests.get(img_path).content
    print("file is",file)
    """Returns web annotations given the path to an image."""
    print('Process Working under ',os.getpid())
    image = types.Image(content=file)
    web_detection = vision_client.web_detection(image=image).web_detection
    report(web_detection)

실행할 때 경고로 표시되고 파이썬이 충돌합니다.

objc[67570]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[67570]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
objc[67567]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[67567]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
objc[67568]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[67568]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
objc[67569]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[67569]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
objc[67571]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[67571]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
objc[67572]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[67572]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.

OSX에 있습니까? 그렇다면 아마도이 버그 보고서 가 힌트를 줄 것입니다.
IonicSolutions

오 예, 저는 OSX를 사용하고 있습니다. 링크를 알려 주셔서 감사합니다.
SriTeja Chilakamarri

여전히 OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES언급 한대로 설정을 시도했지만 여전히 동일한 오류가 발생합니다. @IonicSolutions
SriTeja Chilakamarri

불행히도이 주제에 대한 구체적인 지식이 없습니다. 내가 할 수있는 일은 Google을 사용하여 관련 문제 (예 : 가능한 해결 방법) 를 찾는 것 입니다.
IonicSolutions

1
이는 High Sierra 이후 Apple이 macOS fork()동작을 변경 했기 때문 입니다. 이 OBJC_DISABLE_INITIALIZE_FORK_SAFETY=yes변수는 최신 ObjectiveC 프레임 워크가 현재 기본적으로 적용하는 즉각적인 충돌 동작을 끕니다. 이는 특히 "네이티브 확장"/ C 코드 확장이 사용되는 경우 fork()macOS >= 10.13에서 다중 스레딩 / 다중 처리를 수행하는 모든 언어에 영향을 미칠 수 있습니다 .
TrinitronX

답변:


219

이 오류는 Mac OS High Sierra에서 다중 스레딩을 제한하기 위해 추가 된 보안 때문에 발생합니다. 이 답변이 조금 늦었지만 다음 방법을 사용하여 문제를 해결했습니다.

새로운 Mac OS High Sierra 보안 규칙에 따라 멀티 스레딩 애플리케이션 또는 스크립트를 허용하도록 환경 변수 .bash_profile을 설정합니다.

터미널 열기 :

$ nano .bash_profile

파일 끝에 다음 행을 추가하십시오.

export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

터미널을 저장, 종료, 닫은 후 다시 엽니 다. 환경 변수가 이제 설정되었는지 확인하십시오.

$ env

다음과 유사한 출력이 표시됩니다.

TERM_PROGRAM=Apple_Terminal
SHELL=/bin/bash
TERM=xterm-256color
TMPDIR=/var/folders/pn/vasdlj3ojO#OOas4dasdffJq/T/
Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.E7qLFJDSo/Render
TERM_PROGRAM_VERSION=404
TERM_SESSION_ID=NONE
OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

이제 멀티 스레딩으로 파이썬 스크립트를 실행할 수 있습니다.


12
이것은 실제로 나를 위해 그것을 해결했습니다. 여러 스레드에서 큰 팬더 데이터 프레임을 반복하고 싶었고 작업에서 설명한 것과 동일한 문제가 발생했습니다. 이 답변으로 문제가 해결되었습니다. : 만 차이가 나는 내가 실행 스크립트가 ENV 변수 설정이다OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES python my-script.py
로드리고 - 실 베이라

3
정말 고마워! 관심있는 사람들을 위해 이것은 macOS Mojave에서 저에게 효과적이었습니다.
nmetts

이건 내 문제를 해결했다,하지만 내 스크립트가 멀티 프로세싱을 사용하고 있었다
lollerskates

macOS Mojave가 설치된 컴퓨터에서 작동했지만 내 pytest 테스트가 더 이상 병렬로 실행되지 않습니다. 이전에는 충돌했지만 적어도 빠르다 ...
onekiloparsec

2
이 환경 변수는 내 Mac (catalina)에서 로컬로 ansible을 실행하는 문제를 해결했습니다
itsjwala

0

OBJC_DISABLE_INITIALIZE_FORK_SAFETY환경에서 플래그 없이 나를 위해 작동하는 솔루션 multiprocessing.Poolmain()프로그램이 시작된 직후에 클래스 를 초기화하는 것 입니다.

이것은 가능한 가장 빠른 솔루션이 아닐 가능성이 높으며 모든 상황에서 작동하는지 확실하지 않지만 프로그램이 시작되기 전에 작업자 프로세스를 미리 가열해도 ... may have been in progress in another thread when fork() was called오류가 발생 하지 않으며 성능이 크게 향상됩니다. 병렬화되지 않은 코드로 얻는 것.

나는 Parallelizer아주 일찍 시작한 다음 프로그램의 수명주기 동안 사용 하는 편의 수업 을 만들었습니다 .

# entry point to my program
def main():
    parallelizer = Parallelizer()
    ...

그런 다음 병렬화를 원할 때마다 :

# this function is parallelized. it is run by each child process.
def processing_function(input):
    ...
    return output

...
inputs = [...]
results = parallelizer.map(
    inputs,
    processing_function
)

그리고 병렬화 클래스 :

class Parallelizer:
    def __init__(self):
        self.input_queue = multiprocessing.Queue()
        self.output_queue = multiprocessing.Queue()
        self.pool = multiprocessing.Pool(multiprocessing.cpu_count(),
                                         Parallelizer._run,
                                         (self.input_queue, self.output_queue,))

    def map(self, contents, processing_func):
        size = 0
        for content in contents:
            self.input_queue.put((content, processing_func))
            size += 1
        results = []
        while size > 0:
            result = self.output_queue.get(block=True)
            results.append(result)
            size -= 1
        return results

    @staticmethod
    def _run(input_queue, output_queue):
        while True:
            content, processing_func = input_queue.get(block=True)
            result = processing_func(content)
            output_queue.put(result)

한 가지주의 사항 : 병렬화 된 코드는 디버그하기 어려울 수 있으므로 하위 프로세스에서 문제가 발생할 때 활성화 할 수있는 비 병렬화 버전의 클래스도 준비했습니다.

class NullParallelizer:
    @staticmethod
    def map(contents, processing_func):
        results = []
        for content in contents:
            results.append(processing_func(content))
        return results
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.