实现简单的Future

异步调用的困难之处我们不知道它什么时候会返回,通常的解决方法是使用回调来通知异步调用的结束。在大部分情况下,回调这种方式工作得很好,但是当异步调用太多时,太多的回调会将代码弄得很乱,同时很难跟踪代码执行过程。另外,它也使得在同步调用和异步调用之间切换变得困难。Java中有Future的概念,它封装了一个Callable对象,调用Future的get方法,不管Callable是异步调用还是同步调用,它都会一直等到Callable执行结束。这种方式的好处是程序不再管调用是异步还是同步的,从而极大地简化代码。Future通常是由Executor来创建的,Executor抽象任务的调度过程,是在调用线程中执行,或是另起一个线程,甚至另起一个进程,这些用户都不必关心。

这里我要实现一个简单的ThreadedExecutor,它每次来一个任务(调用它的submit方法)都起一个线程去执行,并立即返回一个Future对象,对Future对象调用result方法会一直等待任务执行结束,并返回执行结果。先从客户端代码写起:

import futures
import urllib2

def load_url(url, timeout):
return urllib2.urlopen(url, timeout=timeout).read()

executor = futures.ThreadedExecutor()
future = executor.submit(load_url, 'http://www.baidu.com', 60)
result = future.result()
print 'result: %s' % result
print 'OK'

上面的代码会另起一个线程抓取百度首页并将结果打印出来,但是从代码上看不到任何线程相关的代码(除了ThreadedExecutor这个名字暗示了可能有另外的线程执行),整个调用看起来就像同步调用。先看ThreadedExecutor的实现:

class ThreadedExecutor(object):
def submit(self, target, *args, **kwargs):
future = Future()
# always start a new thread
thread = threading.Thread(target=_Task(future, target, *args, **kwargs))
thread.start()
return future

ThreadedExecutor的submit方法只是创建一个Future对象,然后创建一个_Task对象,启动另一个线程运行它。

class _Task(object):
def __init__(self, future, target, *args, **kwargs):
self.future = future
self.target = target
self.args = args
self.kwargs = kwargs

def __call__(self):
try:
value = self.target(*self.args, **self.kwargs)
self.future._set_result(value)
except:
import sys
self.future._set_error(sys.exc_info()[0])

_Task对象本身是callable的,因为它实现了__call__方法,因而可以直接传递给线程。它就是简单地执行客户端的任务,完成之后将结果设置到future当中,注意__call__方法会捕捉所以异常,这是为了保证调用future的result()方法时程序总能结束。

class Future(object):
def __init__(self):
self._event = threading.Event()
self._result = None
self._error_happened = False
self._error = None

def result(self):
self._event.wait()
if self._error_happened:
raise self._error
return self._result

def _set_result(self, result):
self._result = result
self._event.set()

def _set_error(self, error):
self._error = error
self._error_happened = True
self._event.set()


Future这里关键是使用Event来协调线程之间的协作, 当调用result()方法时会先调用event的wait方法,它会一直等待直到event的set方法被调用,而只有客户提交的任务结束时才会调用,其总体效果就是result()就是只有当任务执行完成后才会返回。

另外,python3中已经对future已经有[url=http://docs.python.org/dev/library/concurrent.futures.html]内置实现[/url],对python2可以使用[url=http://pypi.python.org/pypi/futures]这个[/url]。

(完)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值