Багатопроцесорна обробка призводить до аварійного завершення роботи Python і видає помилку, можливо, виконувалася в іншому потоці під час виклику fork ()


90

Я відносно новачок у Python і намагаюся реалізувати багатопроцесорний модуль для мого циклу for.

У мене є масив URL-адрес зображень, що зберігаються в img_urls, і мені потрібно завантажити та застосувати деяке бачення Google.

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)

Я отримую це як попередження, коли я запускаю його, і python аварійно завершує роботу

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, дякую, що вказали мені на посилання.
ШріТея Чилакамаррі,

Досі не вдалося спробувати встановити OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YESяк згадано, все одно отримуємо ту ж помилку. @IonicSolutions
SriTeja Chilakamarri

На жаль, я не маю конкретних знань з цієї теми. Все, що я можу зробити, - це використовувати Google для пошуку пов’язаних проблем, наприклад цього можливого обхідного шляху .
IonicSolutions

1
Це пов’язано зі зміною fork()поведінки Apple macOS з часів High Sierra . У OBJC_DISABLE_INITIALIZE_FORK_SAFETY=yesзмінних Виключають негайне поведінку аварії , що їх нова структура ObjectiveC зазвичай нав'язує в даний час за замовчуванням. Це може вплинути на будь-яку мову, яка виконує багатопотоковість / багатопроцесорність за допомогою fork()macOS >= 10.13, особливо коли використовуються "рідні розширення" / розширення коду C.
TrinitronX

Відповіді:


218

Ця помилка виникає через додаткову безпеку для обмеження багатопоточності в Mac OS High Sierra. Я знаю, що ця відповідь трохи запізнилася, але я вирішив проблему, використовуючи такий метод:

Встановіть змінну середовища .bash_profile, щоб дозволити багатопотокові програми або сценарії згідно з новими правилами безпеки Mac OS High Sierra.

Відкрийте термінал:

$ 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

Тепер ви повинні мати можливість запускати скрипт python із багатопоточністю.


12
Це насправді це вирішило для мене. Я хотів повторити великий фрейм даних pandas у декількох потоках і зіткнувся з тією ж проблемою, описаною в роботі. Ця відповідь вирішила проблему для мене. Єдина відмінність полягає в тому, що я встановив цю змінну env за допомогою сценарію, який я запустив:OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES python my-script.py
rodrigo-silveira

3
Дуже дякую! Для зацікавлених це працювало для мене на macOS Mojave.
nmetts

Це вирішило мою проблему, але мій сценарій використовував багатопроцесорну обробку
lollerskates

Це працювало на моїй машині з macOS Mojave, але тоді мої тести pytest більше не виконуються паралельно. Раніше це падало, але принаймні, це було швидко ...
onekiloparsec

1
Ця змінна середовища вирішила мою проблему локального запуску ansible на моєму mac (catalina)
itsjwala

0

Рішення, яке працює для мене без OBJC_DISABLE_INITIALIZE_FORK_SAFETYпрапора в середовищі, передбачає ініціалізацію multiprocessing.Poolкласу відразу після main()запуску програми.

Це, швидше за все, не найшвидше можливе рішення, і я не впевнений, що воно працює у всіх ситуаціях, однак попереднє нагрівання робочих процесів досить рано до мого запуску програм не призводить до ... 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.