Когда объект списка передается процессу и потоку python (3.9), дополнения к объекту списка, сделанные в нить, видны в родительском объекте, но не добавления, сделанные в процесс. например,
from multiprocessing import Process
from threading import Thread
def job(x, out):
out.append(f'f({x})')
out = []
pr = Process(target=job, args=('process', out))
th = Thread(target=job, args=('thread', out))
pr.start(), th.start()
pr.join(), th.join()
print(out)
Это печатает ['f(thread)']
. Я ожидал, что это будет (не обращайте внимания на порядок) ['f(thread)', 'f(process)']
.
Может ли кто-нибудь объяснить причину этого?
В этом нет ничего специфичного для Python; именно так работают процессы.
В частности, все потоки, работающие в данном процессе, совместно используют пространство памяти процесса, так что, например. если поток A изменяет состояние переменной, поток B "увидит" это изменение.
Процессы, OTOH, получают свое личное пространство памяти, недоступное для всех других процессов. Это делается преднамеренно, чтобы предотвратить случайное (или преднамеренное) чтение процессом A или повреждение памяти процесса B.
Когда вы создаете дочерний процесс, новый дочерний процесс получает свое собственное пространство памяти, которое изначально содержит копию всех данных в пространстве памяти родителя, но это отдельное пространство, поэтому изменения, сделанные дочерним процессом, не будут видны для Родитель (и наоборот).
Но теперь я задаюсь вопросом, можно ли вообще доверять моему пониманию потоков и процессов. Итак, прав ли я, выбирая процессы над потоками для параллельного выполнения двух матричных умножений огромный (с интенсивными вычислениями)?
@scribe Специфика python заключается в том, что управление памятью python не является потокобезопасным, поэтому одновременно может выполняться только один поток для каждого процесса. Что касается умножения матриц, то самым быстрым вариантом на самом деле было бы использование numpy
, который в основном написан на c и фортране, и поэтому могу (и работает) запускает более одного потока за раз (и компилируется в машинный код, который быстрее, чем интерпретируемый код)
Я действительно использую Numpy. Но мне нужно выполнить два умножения матриц, которые не зависят друг от друга. Я мог бы исполнить одно, а затем другое, но это заняло бы в два раза больше времени, чем если бы оба могли выполняться одновременно. Я думал, что можно запустить два процесса, каждый из которых выполняет одно умножение одновременно.
@scribe с numpy, либо потоки, либо процессы должны дать вам ускорение. Предположительно, вы захотите, чтобы все результаты были в одном месте в какой-то момент, а это означает, что при маршруте с несколькими процессами вам нужно будет передавать результаты из одного процесса в другой. При многопоточном подходе все ваши результаты уже будут находиться в одном и том же пространстве памяти, что, вероятно, является для вас выигрышем в эффективности, поскольку позволяет избежать шага передачи результатов.
Но, как я понимаю, потоки на самом деле не работают параллельно, они просто жонглируют очень быстро. Вот почему для задач, связанных с вводом-выводом, они являются вариантом, но для процессов, связанных с вычислениями, так как они действительно работают на нескольких ядрах.
Это было бы верно, если бы вы выполняли умножение матрицы с использованием кода Python, но, как отметил @Aaron выше, вычисления Numpy реализованы на C, поэтому ограничение GIL интерпретатора Python на них не влияет.
Ах, не могли бы вы указать мне ресурс, где я мог бы читать дальше?