Параллелизм в Python

Введение

Примеры

  • Потоковый модуль

     from __future__ import print_function
    import threading
    def counter(count):
        while count > 0:
            print("Count value", count)
            count -= 1
        return
    
    t1 = threading.Thread(target=countdown,args=(10,))
    t1.start()
    t2 = threading.Thread(target=countdown,args=(20,))
    t2.start()
    
     

    В некоторых реализациях Python , такие как CPython, истинный параллелизм не достигаются с использованием потоков из - за того, что с помощью известно как Жиль, или Г ЛОБАЛЬНОГО Я nterpreter L Ock.

    Вот отличный обзор параллелизма Python:

    Параллельность Python Дэвида Бизли

  • Многопроцессорный модуль

     from __future__ import print_function
    import multiprocessing
    
    
    def countdown(count):
        while count > 0:
            print("Count value", count)
            count -= 1
        return
    
    if __name__ == "__main__":
        p1 = multiprocessing.Process(target=countdown, args=(10,))
        p1.start()
    
        p2 = multiprocessing.Process(target=countdown, args=(20,))
        p2.start()
    
        p1.join()
        p2.join()
    
     

    Здесь каждая функция выполняется в новом процессе. Так как новый экземпляр Python VM работает код, нет GIL и вы получите параллелизм работает на нескольких ядрах.

    Process.start метод запускает этот новый процесс и запустить функцию передается в target аргументе с аргументами args . Process.join метод ожидает конца выполнения процессов p1 и p2 .

    Новые процессы запускаются по- разному в зависимости от версии питона и на котором цех изготовления печатных форм код работает , например:

    • Windows , использует spawn , чтобы создать новый процесс.
    • В системах UNIX и версии ранее чем 3.3, процессы создаются с помощью fork . Обратите внимание, что этот метод не учитывает использование fork в POSIX и, таким образом, приводит к неожиданному поведению, особенно при взаимодействии с другими многопроцессорными библиотеками.
    • С помощью системы UNIX и версии 3.4+, вы можете выбрать , чтобы начать новые процессы либо с fork , forkserver или spawn , используя multiprocessing.set_start_method в начале программы. forkserver и spawn метода медленнее , чем разветвление , но избежать некоторого неожиданного поведения.

    POSIX вилка использование:

    После разветвления в многопоточной программе дочерний процесс может безопасно вызывать только асинхронно-безопасные функции до тех пор, пока он не вызовет execve. ( См )

    Используя вилку, новый процесс будет запущен с точно таким же состоянием для все текущего мьютекса , но только MainThread будет запущен. Это небезопасно , поскольку это может привести к условиям гонки , например:

    На самом деле, такого рода поведение не должно происходило в чистом питоне , как multiprocessing обрабатывает это правильно , но если вы взаимодействуете с другой библиотеке, такое поведение может встречается , , что привело к краху вашей системы (например , с NumPy / ускорен на MacOS).

  • Передача данных между многопроцессорными процессами

    Поскольку данные чувствительны при обращении между двумя потоками (думаю, что одновременное чтение и параллельная запись могут конфликтовать друг с другом, вызывая условия гонки), был создан набор уникальных объектов, чтобы облегчить передачу данных назад и вперед между потоками. Любая действительно атомарная операция может использоваться между потоками, но всегда безопасно придерживаться очереди.

     import multiprocessing
    import queue
    my_Queue=multiprocessing.Queue() 
    #Creates a queue with an undefined maximum size
    #this can be dangerous as the queue becomes increasingly large
    #it will take a long time to copy data to/from each read/write thread
    
     

    Большинство людей предлагают, чтобы при использовании очереди всегда помещать данные очереди в блок try: exception: вместо использования empty. Однако, для применений , где это не имеет значения , если вы пропустите цикл сканирования (данные могут быть помещены в очередь , пока она подбрасывает состояния от queue.Empty==True для queue.Empty==False ) обычно лучше разместить чтения и записать доступ в то, что я называю блоком Iftry, потому что оператор if более технически эффективен, чем перехват исключения.

    import multiprocessing
    import queue
    '''Import necessary Python standard libraries, multiprocessing for classes and queue for the queue exceptions it provides'''
    def Queue_Iftry_Get(get_queue, default=None, use_default=False, func=None, use_func=False):
        '''This global method for the Iftry block is provided for it's reuse and 
    standard functionality, the if also saves on performance as opposed to catching
     the exception, which is expencive.
            It also allows the user to specify a function for the outgoing data to use,
     and a default value to return if the function cannot return the value from the queue'''
            if get_queue.empty():
                if use_default:
                    return default
            else:
                try:
                    value = get_queue.get_nowait()
                except queue.Empty:
                    if use_default:
                        return default
                else:
                    if use_func:
                        return func(value)
                    else:
                        return value
        def Queue_Iftry_Put(put_queue, value):
            '''This global method for the Iftry block is provided because of its reuse 
    and 
    standard functionality, the If also saves on performance as opposed to catching
     the exception, which is expensive.
            Return True if placing value in the queue was successful. Otherwise, false'''
            if put_queue.full():
                return False
            else:
                try:
                    put_queue.put_nowait(value)
                except queue.Full:
                    return False
                else:
                    return True 

Синтаксис

Параметры

Примечания