đa luồng python chờ cho đến khi tất cả các chủ đề kết thúc


119

Điều này có thể đã được hỏi trong một bối cảnh tương tự nhưng tôi không thể tìm thấy câu trả lời sau khoảng 20 phút tìm kiếm, vì vậy tôi sẽ hỏi.

Tôi đã viết một tập lệnh Python (giả sử: scriptA.py) và một tập lệnh (giả sử scriptB.py)

Trong scriptB tôi muốn gọi scriptA nhiều lần với các đối số khác nhau, mỗi lần mất khoảng một giờ để chạy, (đó là một kịch bản lớn, có rất nhiều thứ .. đừng lo lắng về điều đó) và tôi muốn có thể chạy scriptA với tất cả các đối số khác nhau cùng một lúc, nhưng tôi cần đợi cho đến khi TẤT CẢ chúng được thực hiện trước khi tiếp tục; mã của tôi:

import subprocess

#setup
do_setup()

#run scriptA
subprocess.call(scriptA + argumentsA)
subprocess.call(scriptA + argumentsB)
subprocess.call(scriptA + argumentsC)

#finish
do_finish()

Tôi muốn chạy tất cả subprocess.call()cùng một lúc, và sau đó đợi cho đến khi chúng hoàn thành, tôi nên làm điều này như thế nào?

Tôi đã cố gắng sử dụng luồng như ví dụ ở đây :

from threading import Thread
import subprocess

def call_script(args)
    subprocess.call(args)

#run scriptA   
t1 = Thread(target=call_script, args=(scriptA + argumentsA))
t2 = Thread(target=call_script, args=(scriptA + argumentsB))
t3 = Thread(target=call_script, args=(scriptA + argumentsC))
t1.start()
t2.start()
t3.start()

Nhưng tôi không nghĩ rằng điều này là đúng.

Làm thế nào để tôi biết họ đã chạy xong trước khi đến nhà tôi do_finish()?

Câu trả lời:


150

Bạn cần sử dụng phương thức nối của Threadđối tượng vào cuối đoạn script.

t1 = Thread(target=call_script, args=(scriptA + argumentsA))
t2 = Thread(target=call_script, args=(scriptA + argumentsB))
t3 = Thread(target=call_script, args=(scriptA + argumentsC))

t1.start()
t2.start()
t3.start()

t1.join()
t2.join()
t3.join()

Do đó, chủ đề chính sẽ chờ đến t1,t2t3kết thúc thực hiện.


5
hmmm - gặp khó khăn trong việc hiểu một cái gì đó, lần đầu tiên chạy t1 này, đợi cho đến khi nó kết thúc, sau đó đi đến t2..vv, v.v? Làm thế nào để làm cho tất cả xảy ra cùng một lúc? tôi không thấy làm thế nào điều này sẽ chạy chúng cùng một lúc?
Inbar Rose

25
Cuộc gọi đến joinkhối cho đến khi kết thúc thực hiện. Bạn sẽ phải chờ cho tất cả các chủ đề nào. Nếu t1kết thúc trước tiên, bạn sẽ bắt đầu chờ đợi t2(điều này có thể đã kết thúc và bạn sẽ ngay lập tức tiến hành chờ đợi t3). Nếu t1mất nhiều thời gian nhất để thực thi, khi bạn quay lại từ cả hai t1t2sẽ quay lại ngay lập tức mà không bị chặn.
Maksim Skurydzin

1
bạn không hiểu câu hỏi của tôi - nếu tôi sao chép đoạn mã trên vào mã của mình - nó có hoạt động không? hoặc tôi đang thiếu một cái gì đó?
Inbar Rose

2
được rồi, tôi hiểu rồi Bây giờ tôi đã hiểu, có một chút bối rối về nó nhưng tôi nghĩ rằng tôi hiểu, joinsắp xếp quá trình hiện tại vào luồng và đợi cho đến khi nó hoàn thành, và nếu t2 kết thúc trước t1 thì khi t1 hoàn thành, nó sẽ kiểm tra xem t2 đã xong chưa đó là, và sau đó kiểm tra t3..etc..v.v .. và sau đó chỉ khi tất cả được thực hiện, nó sẽ tiếp tục. tuyệt vời.
Inbar Rose

3
nói t1 mất nhiều thời gian nhất, nhưng t2 có một ngoại lệ. điều gì xảy ra sau đó? bạn có thể bắt ngoại lệ đó hay kiểm tra xem t2 đã hoàn thành hay chưa?
Ciprian Tomoiagă

171

Đặt các chủ đề trong một danh sách và sau đó sử dụng phương thức Tham gia

 threads = []

 t = Thread(...)
 threads.append(t)

 ...repeat as often as necessary...

 # Start all threads
 for x in threads:
     x.start()

 # Wait for all of them to finish
 for x in threads:
     x.join()

1
Vâng, điều đó sẽ làm việc nhưng khó hiểu hơn. Bạn phải luôn cố gắng tìm sự cân bằng giữa mã nhỏ gọn và "khả năng đọc". Hãy nhớ rằng: Mã được viết một lần nhưng đọc nhiều lần. Vì vậy, điều quan trọng hơn là nó dễ hiểu.
Aaron Digulla

2
"Mẫu nhà máy" không phải là thứ tôi có thể giải thích trong một câu. Google cho nó và tìm kiếm stackoverflow.com. Có rất nhiều ví dụ và giải thích. Tóm lại: Bạn viết mã xây dựng một cái gì đó phức tạp cho bạn. Giống như một nhà máy thực sự: Bạn đưa ra một đơn đặt hàng và nhận lại một sản phẩm hoàn chỉnh.
Aaron Digulla

18
Tôi không thích ý tưởng sử dụng khả năng hiểu danh sách cho các tác dụng phụ của nó và không làm gì hữu ích với danh sách kết quả. Một vòng lặp đơn giản sẽ sạch hơn ngay cả khi nó trải rộng hai hàng ...
Ioan Alexandru Cucu

1
@Aaron DIgull Tôi hiểu điều đó. Ý tôi là tôi sẽ chỉ làm một việc for x in threads: x.join()thay vì sử dụng danh sách toàn diện
Ioan Alexandru Cucu

1
@IoanAlexandruCucu: Tôi vẫn đang tự hỏi liệu có giải pháp nào dễ đọc và hiệu quả hơn không: stackoverflow.com/questions/21428602/ Kẻ
Aaron Digulla

29

Trong Python3, vì Python 3.2 có một cách tiếp cận mới để đạt được kết quả tương tự, mà cá nhân tôi thích cách tạo / bắt đầu / tham gia truyền thống, gói concurrent.futures: https://docs.python.org/3/l Library / conản.futures .html

Sử dụng ThreadPoolExecutormã sẽ là:

from concurrent.futures.thread import ThreadPoolExecutor
import time

def call_script(ordinal, arg):
    print('Thread', ordinal, 'argument:', arg)
    time.sleep(2)
    print('Thread', ordinal, 'Finished')

args = ['argumentsA', 'argumentsB', 'argumentsC']

with ThreadPoolExecutor(max_workers=2) as executor:
    ordinal = 1
    for arg in args:
        executor.submit(call_script, ordinal, arg)
        ordinal += 1
print('All tasks has been finished')

Đầu ra của mã trước đó là một cái gì đó như:

Thread 1 argument: argumentsA
Thread 2 argument: argumentsB
Thread 1 Finished
Thread 2 Finished
Thread 3 argument: argumentsC
Thread 3 Finished
All tasks has been finished

Một trong những lợi thế là bạn có thể kiểm soát thông lượng cài đặt các công nhân đồng thời tối đa.


nhưng làm thế nào bạn có thể biết khi tất cả các chủ đề trong luồng đã hoàn thành?
Prime By Design

1
Như bạn có thể thấy trong ví dụ, mã sau khi withcâu lệnh được thực thi khi tất cả các tác vụ đã kết thúc.
Roberto

cái này không hoạt động. Hãy thử làm một cái gì đó thực sự dài trong chủ đề. Tuyên bố in của bạn sẽ thực thi trước khi kết thúc chuỗi
Pranalee

@Pranalee, Mã đó hoạt động, tôi đã cập nhật mã để thêm các dòng đầu ra. Bạn không thể xem "Tất cả tác vụ ..." trước khi tất cả các luồng kết thúc, Đó là cách withcâu lệnh hoạt động theo thiết kế trong trường hợp này. Dù sao, bạn luôn có thể mở một câu hỏi mới trong SO và đăng mã của bạn để chúng tôi có thể giúp bạn tìm hiểu những gì đang xảy ra trong trường hợp của bạn.
Roberto

@PrimeByDesign bạn có thể sử dụng concurrent.futures.waitchức năng, bạn có thể thấy một ví dụ thực tế ở đây tài liệu chính thức: docs.python.org/3/library/...
Alexander Fortin

28

Tôi thích sử dụng hiểu danh sách dựa trên danh sách đầu vào:

inputs = [scriptA + argumentsA, scriptA + argumentsB, ...]
threads = [Thread(target=call_script, args=(i)) for i in inputs]
[t.start() for t in threads]
[t.join() for t in threads]

Kiểm tra câu trả lời giải thích tốt nhưng câu này ngắn hơn và không yêu cầu lặp lại xấu xí. Chỉ là một câu trả lời tốt đẹp. :)
tleb

Danh sách hiểu chỉ cho tác dụng phụ thường được khấu hao *. Nhưng trong trường hợp sử dụng này, nó có vẻ là một ý tưởng tốt. * stackoverflow.com/questions/5753597/ từ
Vinayak Kaniyarakkal

3
@VinayakKaniyarakkal for t in threads:t.start()không tốt hơn sao?
SmartManoj

5

Bạn có thể có một lớp nào đó như bên dưới mà từ đó bạn có thể thêm số lượng hàm hoặc bảng điều khiển bạn muốn thực hiện trong niềm đam mê song song và bắt đầu thực hiện và chờ cho tất cả các công việc hoàn thành ..

from multiprocessing import Process

class ProcessParallel(object):
    """
    To Process the  functions parallely

    """    
    def __init__(self, *jobs):
        """
        """
        self.jobs = jobs
        self.processes = []

    def fork_processes(self):
        """
        Creates the process objects for given function deligates
        """
        for job in self.jobs:
            proc  = Process(target=job)
            self.processes.append(proc)

    def start_all(self):
        """
        Starts the functions process all together.
        """
        for proc in self.processes:
            proc.start()

    def join_all(self):
        """
        Waits untill all the functions executed.
        """
        for proc in self.processes:
            proc.join()


def two_sum(a=2, b=2):
    return a + b

def multiply(a=2, b=2):
    return a * b


#How to run:
if __name__ == '__main__':
    #note: two_sum, multiply can be replace with any python console scripts which
    #you wanted to run parallel..
    procs =  ProcessParallel(two_sum, multiply)
    #Add all the process in list
    procs.fork_processes()
    #starts  process execution 
    procs.start_all()
    #wait until all the process got executed
    procs.join_all()

Đây là đa xử lý. Câu hỏi là về docs.python.org/3/l Library / threading.html
Rustam A.

3

Từ threading tài liệu mô-đun

Có một đối tượng chủ đề chính của người Viking; cái này tương ứng với luồng điều khiển ban đầu trong chương trình Python. Nó không phải là một chủ đề daemon.

Có khả năng các đối tượng chủ đề giả hình tinh tinh được tạo ra. Đây là các đối tượng luồng tương ứng với các luồng ngoài hành tinh của người Viking, là các luồng điều khiển được bắt đầu bên ngoài mô-đun luồng, chẳng hạn như trực tiếp từ mã C. Đối tượng chủ đề giả có chức năng hạn chế; chúng luôn được coi là còn sống và daemon, và không thể được chỉnh sửa join(). Chúng không bao giờ bị xóa, vì không thể phát hiện sự chấm dứt của các chủ đề ngoài hành tinh.

Vì vậy, để nắm bắt hai trường hợp đó khi bạn không quan tâm đến việc giữ một danh sách các chủ đề bạn tạo:

import threading as thrd


def alter_data(data, index):
    data[index] *= 2


data = [0, 2, 6, 20]

for i, value in enumerate(data):
    thrd.Thread(target=alter_data, args=[data, i]).start()

for thread in thrd.enumerate():
    if thread.daemon:
        continue
    try:
        thread.join()
    except RuntimeError as err:
        if 'cannot join current thread' in err.args[0]:
            # catchs main thread
            continue
        else:
            raise

Trong đó:

>>> print(data)
[0, 4, 12, 40]

2

Có lẽ, một cái gì đó như

for t in threading.enumerate():
    if t.daemon:
        t.join()

Tôi đã thử mã này nhưng không chắc chắn về hoạt động của nó bởi vì hướng dẫn cuối cùng của mã của tôi đã được in sau vòng lặp này và quy trình vẫn chưa kết thúc.
Omkar

1

Tôi vừa gặp một vấn đề tương tự khi tôi cần đợi tất cả các luồng được tạo bằng vòng lặp for. Tôi chỉ thử đoạn mã sau. Đây có thể không phải là giải pháp hoàn hảo nhưng tôi nghĩ đó sẽ là một giải pháp đơn giản để kiểm tra:

for t in threading.enumerate():
    try:
        t.join()
    except RuntimeError as err:
        if 'cannot join current thread' in err:
            continue
        else:
            raise
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.