Chính xác thì Phương thức .join () của Mô-đun đa xử lý Python đang làm gì?


110

Tìm hiểu về Đa xử lý trong Python (từ một bài báo PMOTW ) và muốn được giải thích rõ ràng về chính xác join()phương pháp này đang làm gì.

Trong một hướng dẫn cũ từ năm 2008, nó nói rằng nếu không có lệnh p.join()gọi trong đoạn mã bên dưới, "tiến trình con sẽ không hoạt động và không kết thúc, trở thành một thây ma bạn phải giết theo cách thủ công".

from multiprocessing import Process

def say_hello(name='world'):
    print "Hello, %s" % name

p = Process(target=say_hello)
p.start()
p.join()

Tôi đã thêm một bản in PIDcũng như một bản in time.sleepđể kiểm tra và theo như tôi có thể nói, quá trình tự kết thúc:

from multiprocessing import Process
import sys
import time

def say_hello(name='world'):
    print "Hello, %s" % name
    print 'Starting:', p.name, p.pid
    sys.stdout.flush()
    print 'Exiting :', p.name, p.pid
    sys.stdout.flush()
    time.sleep(20)

p = Process(target=say_hello)
p.start()
# no p.join()

trong vòng 20 giây:

936 ttys000    0:00.05 /Library/Frameworks/Python.framework/Versions/2.7/Reso
938 ttys000    0:00.00 /Library/Frameworks/Python.framework/Versions/2.7/Reso
947 ttys001    0:00.13 -bash

sau 20 giây:

947 ttys001    0:00.13 -bash

Hành vi giống nhau khi p.join()được thêm lại vào cuối tệp. Mô-đun Python trong tuần cung cấp giải thích rất dễ đọc về mô-đun ; "Để đợi cho đến khi một quá trình hoàn thành công việc và thoát ra, hãy sử dụng phương thức join ().", Nhưng có vẻ như ít nhất OS X đã làm điều đó.

Tôi cũng đang thắc mắc về tên của phương pháp. Là .join()phương pháp concatenating bất cứ điều gì ở đây? Nó có nối một quá trình với nó kết thúc không? Hay nó chỉ dùng chung một tên với .join()phương thức gốc của Python ?


2
Theo như tôi biết, nó giữ luồng chính và đợi quy trình con hoàn thành rồi nối lại các tài nguyên trong luồng chính, chủ yếu là thoát sạch.
abhishekgarg

à điều đó có lý. Vì vậy, nó thực sự CPU, Memory resourcesđang được tách khỏi quy trình mẹ, sau đó được chỉnh sửa jointrở lại sau khi quy trình con đã hoàn thành?
MikeiLL

vâng, đó là những gì nó đang làm. Vì vậy, nếu bạn không tham gia cùng họ trở lại, khi quá trình con xong nó chỉ nói dối như một quá trình không còn tồn tại hay đã chết
abhishekgarg

@abhishekgarg Điều đó không đúng. Các quy trình con sẽ được tham gia ngầm khi quy trình chính hoàn thành.
dano

@dano, tôi cũng đang học python và tôi vừa chia sẻ những gì tôi tìm thấy trong các bài kiểm tra của mình, trong các bài kiểm tra của tôi, tôi có một quy trình chính không bao giờ kết thúc nên có thể đó là lý do tại sao tôi thấy các quy trình con đó không còn tồn tại.
abhishekgarg

Câu trả lời:


125

Các join()phương pháp, khi được sử dụng với threadinghoặc multiprocessing, không liên quan đến str.join()- nó không thực sự concatenating bất cứ điều gì với nhau. Đúng hơn, nó chỉ có nghĩa là "đợi [luồng / quy trình] này hoàn thành". Tên joinnày được sử dụng vì multiprocessingAPI của mô-đun có nghĩa là trông giống với threadingAPI của mô-đun và threadingmô-đun sử dụng joincho Threadđối tượng của nó . Việc sử dụng thuật ngữ này joincó nghĩa là "đợi một chuỗi hoàn thành" phổ biến trên nhiều ngôn ngữ lập trình, vì vậy Python cũng đã áp dụng nó.

Bây giờ, lý do bạn thấy độ trễ 20 giây cả khi có và không có lệnh gọi đến join()là vì theo mặc định, khi tiến trình chính sẵn sàng thoát, nó sẽ ngầm gọi join()tất cả các multiprocessing.Processphiên bản đang chạy . Điều này không được nêu rõ ràng trong multiprocessingtài liệu nhưng nó được đề cập trong phần Nguyên tắc lập trình :

Cũng nên nhớ rằng các quy trình không phải daemonic sẽ tự động được tham gia.

Bạn có thể ghi đè lên hành vi này bằng cách thiết lập daemoncờ trên Processđể Truetrước khi bắt đầu quá trình này:

p = Process(target=say_hello)
p.daemon = True
p.start()
# Both parent and child will exit here, since the main process has completed.

Nếu bạn làm điều đó, quy trình con sẽ bị chấm dứt ngay sau khi quy trình chính hoàn tất :

daemon

Cờ daemon của tiến trình, một giá trị Boolean. Điều này phải được đặt trước khi start () được gọi.

Giá trị ban đầu được kế thừa từ quá trình tạo.

Khi một quá trình thoát ra, nó sẽ cố gắng chấm dứt tất cả các quá trình con daemonic của nó.


6
Tôi hiểu đó p.daemon=Truelà để "bắt đầu một quy trình nền chạy mà không chặn chương trình chính thoát ra". Nhưng nếu "Tiến trình daemon tự động kết thúc trước khi chương trình chính thoát ra", thì chính xác công dụng của nó là gì?
MikeiLL

8
@MikeiLL Về ​​cơ bản, bất cứ thứ gì bạn muốn đều diễn ra trong nền miễn là tiến trình mẹ đang chạy, nhưng điều đó không cần phải được dọn dẹp một cách khéo léo trước khi thoát khỏi chương trình chính. Có lẽ một quy trình công nhân đọc dữ liệu từ một ổ cắm hoặc thiết bị phần cứng và cung cấp dữ liệu đó trở lại nguồn cấp dữ liệu gốc thông qua hàng đợi hoặc xử lý nó trong nền cho một số mục đích? Nói chung, tôi muốn nói rằng việc sử dụng daemonicquy trình con không an toàn lắm, bởi vì tiến trình sẽ bị chấm dứt mà không cho phép dọn dẹp bất kỳ tài nguyên mở nào mà nó có thể có .. (tt).
dano

7
@MikeiLL Một phương pháp hay hơn là ra hiệu cho đứa trẻ dọn dẹp và thoát ra trước khi thoát khỏi quy trình chính. Bạn có thể nghĩ rằng sẽ hợp lý nếu để quy trình con daemonic chạy khi trình cha mẹ thoát ra, nhưng hãy nhớ rằng multiprocessingAPI được thiết kế để bắt chước threadingAPI càng chặt chẽ càng tốt. Các threading.Threadđối tượng daemonic được kết thúc ngay sau khi luồng chính thoát ra, vì vậy multiprocesing.Processcác đối tượng daemonic hoạt động theo cùng một cách.
dano

38

Nếu không có join(), quy trình chính có thể hoàn thành trước khi quy trình con thực hiện. Tôi không chắc trong những trường hợp nào dẫn đến chủ nghĩa xác sống.

Mục đích chính join()là để đảm bảo rằng một quy trình con đã hoàn thành trước khi quy trình chính thực hiện bất cứ điều gì phụ thuộc vào công việc của quy trình con.

Từ nguyên của join()nó là ngược lại với fork, là thuật ngữ phổ biến trong hệ điều hành Unix-family để tạo các tiến trình con. Một quy trình duy nhất "chia" thành một số, sau đó "nối" lại thành một.


2
Nó sử dụng tên join()join()là thứ được sử dụng để đợi một threading.Threadđối tượng hoàn thành và multiprocessingAPI có nghĩa là bắt chước threadingAPI càng nhiều càng tốt.
dano

Tuyên bố thứ hai của bạn giải quyết vấn đề tôi đang giải quyết trong một dự án hiện tại.
MikeiLL

Tôi hiểu phần trong đó luồng chính chờ quá trình phụ hoàn thành, nhưng điều đó không đánh bại mục đích của việc thực thi Không đồng bộ sao? Nó không được cho là kết thúc việc thực thi, một cách độc lập (tác vụ con hoặc quy trình)?
Apurva Kunkulol

1
@ApurvaKunkulol Tùy thuộc vào cách bạn sử dụng nó, nhưng join()cần thiết trong trường hợp luồng chính cần kết quả công việc của các tiểu trình. Ví dụ: nếu bạn đang kết xuất một thứ gì đó và gán 1/4 hình ảnh cuối cùng cho mỗi quy trình trong số 4 quy trình con, và muốn hiển thị toàn bộ hình ảnh khi hoàn tất.
Russell Borogove

@RussellBorogove Ah! Tôi hiểu rồi. Sau đó, ý nghĩa của hoạt động không đồng bộ là một chút khác nhau ở đây. Nó phải chỉ có nghĩa là thực tế là các quy trình phụ có nghĩa là thực hiện các nhiệm vụ của chúng đồng thời với luồng chính trong khi luồng chính cũng thực hiện công việc của nó thay vì chỉ ngồi chờ đợi các quy trình phụ.
Apurva Kunkulol

12

Tôi sẽ không giải thích chi tiết những gì joincó, nhưng đây là từ nguyên và trực giác đằng sau nó, sẽ giúp bạn nhớ ý nghĩa của nó dễ dàng hơn.

Ý tưởng là thực hiện " dĩa " vào nhiều quá trình trong đó một là bậc thầy, các công nhân còn lại (hoặc "nô lệ"). Khi các công nhân làm xong, chúng "tham gia" với tổng thể để quá trình thực thi nối tiếp có thể được tiếp tục.

Các joinphương pháp làm cho quá trình tổng thể để chờ đợi một người lao động để tham gia nó. Phương thức tốt hơn có thể được gọi là "wait", vì đó là hành vi thực tế mà nó gây ra trong master (và đó là những gì nó được gọi trong POSIX, mặc dù các luồng POSIX cũng gọi nó là "join"). Việc liên kết chỉ xảy ra khi các chủ đề hợp tác với nhau một cách hợp lý, nó không phải là điều mà chủ đề làm .

Các tên "fork" và "join" đã được sử dụng với ý nghĩa này trong đa xử lý kể từ năm 1963 .


Vì vậy, theo một cách nào đó, cách sử dụng từ này joincó thể có trước nó được sử dụng để chỉ sự nối, trái ngược với cách khác.
MikeiLL

1
Không chắc rằng việc sử dụng trong nối bắt nguồn từ việc sử dụng trong đa xử lý; thay vào đó, cả hai nghĩa đều có nguồn gốc tách biệt với nghĩa đơn giản của từ này trong tiếng Anh.
Russell Borogove

2

join()được sử dụng để đợi các quy trình worker thoát ra. Người ta phải gọi close()hoặc terminate()trước khi sử dụng join().

Giống như @Russell được đề cập, tham gia giống như ngược lại với fork (sinh ra các quy trình phụ).

Để tham gia chạy, bạn phải chạy điều close()này sẽ ngăn không cho bất kỳ nhiệm vụ nào khác được gửi vào nhóm và thoát ra sau khi tất cả các nhiệm vụ hoàn thành. Ngoài ra, đang chạy terminate()sẽ chỉ thoát ra bằng cách dừng tất cả các quy trình của worker ngay lập tức.

"the child process will sit idle and not terminate, becoming a zombie you must manually kill" điều này có thể xảy ra khi quy trình chính (cha) thoát ra nhưng quy trình con vẫn đang chạy và sau khi hoàn thành, quy trình mẹ không có quy trình cha nào để trả lại trạng thái thoát của nó.


2

Lệnh join()gọi đảm bảo rằng các dòng mã tiếp theo của bạn không được gọi trước khi tất cả các quá trình đa xử lý được hoàn thành.

Ví dụ: nếu không có join(), mã sau sẽ gọi restart_program()ngay cả trước khi quá trình kết thúc, điều này tương tự như không đồng bộ và không phải là những gì chúng tôi muốn (bạn có thể thử):

num_processes = 5

for i in range(num_processes):
    p = multiprocessing.Process(target=calculate_stuff, args=(i,))
    p.start()
    processes.append(p)
for p in processes:
    p.join() # call to ensure subsequent line (e.g. restart_program) 
             # is not called until all processes finish

restart_program()

0

Để đợi cho đến khi một tiến trình hoàn thành công việc và thoát ra, hãy sử dụng phương thức join ().

Lưu ý Điều quan trọng là tham gia () quá trình sau khi kết thúc nó để cho máy móc nền có thời gian cập nhật trạng thái của đối tượng để phản ánh việc kết thúc.

Đây là một ví dụ tốt đã giúp tôi hiểu nó: tại đây

Một điều cá nhân tôi nhận thấy là quy trình chính của tôi bị tạm dừng cho đến khi đứa trẻ hoàn thành quá trình của nó bằng cách sử dụng phương thức join () đã đánh bại điểm mà tôi sử dụng multiprocessing.Process()ngay từ đầu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.