Python:如何使程序等待函数或方法完成

5 投票
2 回答
64552 浏览
提问于 2025-04-17 22:44

有时候,程序需要等一个函数完成它的工作。还有时候,主程序不需要等待。这里有一个简单的例子。总共有四个按钮,点击每个按钮都会调用同一个calculate()函数。唯一的不同在于调用这个函数的方式。

  1. “直接调用”按钮直接调用calculate()函数。因为有一个“函数结束”的打印输出,所以很明显程序在等待calculate函数完成它的工作。
  2. “通过线程调用”按钮这次使用线程机制来调用同一个函数。因为程序在按钮被按下后立即打印出“函数结束”的信息,所以我可以得出结论,程序并没有等待calculate()函数完成。那怎么才能改变这种行为呢?怎么让程序等到calculate()函数完成后再继续?
  3. “通过多进程调用”按钮利用多进程来调用calculate()函数。就像使用线程一样,多进程也不会等待函数完成。我们需要加什么语句才能让它等待呢?

  4. “通过子进程调用”按钮什么也没做,因为我还没找到方法把子进程连接起来以运行内部脚本函数或方法。看到怎么做会很有趣……

示例:

from PyQt4 import QtCore, QtGui    
app = QtGui.QApplication(sys.argv)


def calculate(listArg=None):
    print '\n\t Starting calculation...'
    m=0
    for i in range(50000000):
        m+=i
    print '\t ...calculation completed\n'


class Dialog_01(QtGui.QMainWindow):
    def __init__(self):
        super(Dialog_01, self).__init__()

        myQWidget = QtGui.QWidget()
        myBoxLayout = QtGui.QVBoxLayout()       

        directCall_button = QtGui.QPushButton("Call Directly")
        directCall_button.clicked.connect(self.callDirectly)      
        myBoxLayout.addWidget(directCall_button) 

        Button_01 = QtGui.QPushButton("Call via Threading")
        Button_01.clicked.connect(self.callUsingThreads)
        myBoxLayout.addWidget(Button_01)        

        Button_02 = QtGui.QPushButton("Call via Multiprocessing")
        Button_02.clicked.connect(self.callUsingMultiprocessing)
        myBoxLayout.addWidget(Button_02) 

        Button_03 = QtGui.QPushButton("Call via Subprocess")
        Button_03.clicked.connect(self.callUsingSubprocess)
        myBoxLayout.addWidget(Button_03) 


        myQWidget.setLayout(myBoxLayout)
        self.setCentralWidget(myQWidget)
        self.setWindowTitle('Dialog 01')

    def callUsingThreads(self):
        print '------------------------------- callUsingThreads() ----------------------------------'
        import threading
        self.myEvent=threading.Event()
        self.c_thread=threading.Thread(target=calculate)
        self.c_thread.start()  

        print "\n\t\t : Function End"


    def callUsingMultiprocessing(self):
        print '------------------------------- callUsingMultiprocessing() ----------------------------------'
        from multiprocessing import Pool

        pool = Pool(processes=3)
        try: pool.map_async( calculate, ['some'])
        except Exception, e: print e 

        print "\n\t\t : Function End"


    def callDirectly(self):
        print '------------------------------- callDirectly() ----------------------------------'
        calculate()
        print "\n\t\t : Function End"


    def callUsingSubprocess(self):
        print '------------------------------- callUsingSubprocess() ----------------------------------'
        import subprocess 
        print '-missing code solution'
        print "\n\t\t : Function End"


if __name__ == '__main__':
    dialog_1 = Dialog_01()
    dialog_1.show()
    dialog_1.resize(480,320)
    sys.exit(app.exec_())

2 个回答

1

我发现,在Python脚本中使用“multiprocessing”模块里的“pool”子模块,可以很方便地同时执行多个进程。

查看:使用工作池

仔细看看示例中的“# 启动多个异步评估 可能 使用更多进程”这句话。一旦你理解了这些代码的作用,接下来我构建的示例就会变得非常清晰。

import numpy as np
from multiprocessing import Pool

def desired_function(option, processes, data, etc...):
    # your code will go here. option allows you to make choices within your script
    # to execute desired sections of code for each pool or subprocess.

    return result_array   # "for example"


result_array = np.zeros("some shape")  # This is normally populated by 1 loop, lets try 4.
processes = 4
pool = Pool(processes=processes)
args = (processes, data, etc...)    # Arguments to be passed into desired function.

multiple_results = []
for i in range(processes):          # Executes each pool w/ option (1-4 in this case).
    multiple_results.append(pool.apply_async(param_process, (i+1,)+args)) # Syncs each.

results = np.array(res.get() for res in multiple_results)  # Retrieves results after
                                                           # every pool is finished!

for i in range(processes):
    result_array = result_array + results[i]  # Combines all datasets!

这段代码基本上会在设定的进程数量下运行你想要的函数。你需要确保你的函数能够区分每个进程(这就是我添加“option”变量的原因)。另外,最后不一定要填充一个数组,但在我的示例中就是这样使用的。希望这能让你更简单地理解Python中多进程的强大之处!

8

使用队列:每个线程完成后会把结果放到队列里,然后你只需要读取适量的结果,其他的可以忽略掉。

#!python3.3
import queue    # For Python 2.x use 'import Queue as queue'
import threading, time, random

def func(id, result_queue):
    print("Thread", id)
    time.sleep(random.random() * 5)
    result_queue.put((id, 'done'))

def main():
    q = queue.Queue()
    threads = [ threading.Thread(target=func, args=(i, q)) for i in range(5) ]
    for th in threads:
        th.daemon = True
        th.start()

    result1 = q.get()
    result2 = q.get()

    print("Second result: {}".format(result2))

if __name__=='__main__':
    main()

关于Queue.get()的说明(不带参数时,它等同于Queue.get(True, None)):

    Queue.get([block[, timeout]])

Remove and return an item from the queue. If optional args block is true and timeout is None (the default), block if necessary until an item is available. If timeout is a positive number, it blocks at most timeout seconds and raises the Empty exception if no item was available within that time. Otherwise (block is false), return an item if one is immediately available, else raise the Empty exception (timeout is ignored in that case).

如何在Python中等待直到第一个线程完成

你也可以使用.join()方法。 join()在Python线程中的作用是什么

撰写回答