0

我有两个不同的函数fg用不同的算法计算相同的结果。有时一个或另一个需要很长时间,而另一个很快就会终止。我想创建一个同时运行每个函数的新函数,然后从第一个完成的函数返回结果。

我想用高阶函数创建那个函数

h = firstresult(f, g)

在 Python 中完成此任务的最佳方法是什么?

我怀疑解决方案涉及线程。我想避免讨论 GIL。

4

4 回答 4

2

我会为此简单地使用队列。启动线程,第一个有结果的线程写入队列。

代码

from threading import Thread
from time import sleep
from Queue import Queue

def firstresult(*functions):
    queue = Queue()
    threads = [] 
    for f in functions:
        def thread_main():
            queue.put(f())
        thread = Thread(target=thread_main)
        threads.append(thread)
        thread.start()
    result = queue.get()
    return result

def slow():
    sleep(1)
    return 42 

def fast():
    return 0

if __name__ == '__main__':
    print firstresult(slow, fast)

现场演示

http://ideone.com/jzzZX2

笔记

停止线程是一个完全不同的话题。为此,您需要向state需要定期检查的线程添加一些变量。由于我想保持这个例子简短,我只是假设这部分,并假设所有工人都有时间完成他们的工作,即使结果从未被读取。

按照提问者的要求跳过关于 Gil 的讨论。;-)

于 2013-02-08T01:14:08.143 回答
1

在一个新的工作线程中运行每个函数,两个工作线程将结果发送回一个 1 项队列或类似的东西中的主线程。当主线程从获胜者那里收到结果时,它会杀死(python 线程是否支持杀死?哈哈)两个工作线程以避免浪费时间(一个函数可能需要几个小时,而另一个只需要一秒钟)。

如果需要,将单词 thread 替换为 process。

于 2013-02-08T00:58:50.387 回答
1

您将需要在另一个进程(使用多处理)或不同的线程中运行每个函数。如果两者都受 CPU 限制,那么多线程将有很大帮助——正是由于 GIL——所以多处理是一种方式。

如果返回值是一个可腌制(可序列化)对象,我创建了这个装饰器,它只是在后台运行该函数,在另一个进程中:

https://bitbucket.org/jsbueno/lelo/src

这并不完全是您想要的 - 因为两者都是非阻塞的并且立即开始执行。这个装饰器的问题是当你尝试使用返回值时它会阻塞(并等待函数完成)。

但另一方面 - 它只是一个完成所有工作的装饰器。

于 2013-02-08T01:00:09.677 回答
1

现在 - 与我对其他答案的建议不同,这段代码完全符合您的要求:

from multiprocessing import Process, Queue

import random
import time


def firstresult(func1, func2):
    queue = Queue()
    proc1 = Process(target=func1,args=(queue,))
    proc2 = Process(target=func2, args=(queue,))
    proc1.start();proc2.start()
    result = queue.get()
    proc1.terminate(); proc2.terminate()
    return result

def algo1(queue):
    time.sleep(random.uniform(0,1))
    queue.put("algo 1")

def algo2(queue):
    time.sleep(random.uniform(0,1))
    queue.put("algo 2")

print firstresult(algo1, algo2)
于 2013-02-08T04:36:48.203 回答