我的python Twisted Klein Web服务中有两个函数:
@inlineCallbacks
def logging(data):
ofile = open("file", "w")
ofile.write(data)
yield os.system("command to upload the written file")
@APP.route('/dostuff')
@inlineCallbacks
def dostuff():
yield logging(data)
print "check!"
returnValue("42")
当os.system("command to upload the written file")
运行时,它会显示消息说"开始上传"然后"上传完成"。我想使日志记录函数异步,以便在logging
处理程序打印出来之后dostuff
处理程序中的处理发生" check!"。 (我实际上希望在returnValue(" 42")之后进行处理,但是我认为这两者都使得记录功能异步?)
我认为yield语句会使它无阻塞,但似乎并非如此,"检查!"总是在&#34之后打印;开始上传"和#34;上传完成"。我很感激是否有人可以给我一些反馈意见,因为我是异步编码的新手并且暂时被阻止了...
答案 0 :(得分:3)
要使代码异步,您需要将Twisted Deferreds用作described here。 Deferreds为您提供了异步代码执行的API,它们允许您将回调附加到您的函数,并且它们在reactor对象管理的Twisted事件循环中执行代码。
我认为在你的案例中有两种可能的方法来使用Deferreds。
1)使用reactor.callLater()
如果dostuff
处理程序不关心结果,这是可以的。您可以使用reactor.callLater()。这样,从doStuff
返回值后,您的异步函数将执行。
这样的事情:
from klein import run, route, Klein
from twisted.internet import defer, task, reactor
import os
app = Klein()
def logging(data):
ofile = open("file", "w")
ofile.write(data)
result = os.system("ls")
print(result)
@route('/')
def dostuff(request):
reactor.callLater(0, logging, "some data")
print("check!")
return b'Hello, world!'
run("localhost", 8080)
使用此代码的事件顺序如下,首先打印“check”,然后返回“hello world”响应,最后异步调用超过并打印运行os.system()
的结果。
2016-08-11 08:52:33+0200 [-] check!
2016-08-11 08:52:33+0200 [-] "127.0.0.1" - - [11/Aug/2016:06:52:32 +0000] "GET / HTTP/1.1" 200 13 "-" "curl/7.35.0"
a.py file
2)在后台执行任务并使用task.deferLater()
如果您关心“日志记录”功能的结果,您还可以将回调附加到此对象并使用twisted.internet.task API。如果你想这样做,你需要重构你的处理程序才能像这样工作
@route('/')
def dostuff(request):
def the_end(result):
print("executed at the end with result: {}".format(result))
dfd = task.deferLater(reactor, 0, logging, "some data")
dfd.addCallback(the_end)
print("check!")
return b'Hello, world!'
这种方式的事件顺序与上面相同,但the_end
函数将在logging
函数完成后执行。
2016-08-11 08:59:24+0200 [-] check!
2016-08-11 08:59:24+0200 [-] "127.0.0.1" - - [11/Aug/2016:06:59:23 +0000] "GET / HTTP/1.1" 200 13 "-" "curl/7.35.0"
a.py file
2016-08-11 08:59:24+0200 [-] executed at the end with result: some result
答案 1 :(得分:1)
'收益率'声明并不能让事情异步发生。它只是推迟包含它的函数的执行,并返回一个生成器对象,以后可以用来迭代序列。
所以dostuff()将返回一个生成器对象。在生成器对象稍后迭代之前不会发生任何事情。但是你的代码中没有任何东西可以实现这一点。我希望你的dostuff例程会产生语法错误,因为它包含yield和非空返回。记录例程不会做任何事情,因为它包含一个yield,它返回的生成器从未使用过。
最后,日志记录例程将在每次调用时截断其输出文件,因为它以模式' w'打开日志文件。每次调用。
对于异步执行,您需要某种形式的多处理。但我不认为在这种情况下需要这样做。您的记录功能相当轻,应该快速运行,不会影响dostuff的工作。
我建议尝试这样的事情:
@inlineCallbacks
def logging(data):
try:
logging._ofile.write(data + '\n')
except AttributeError:
logging._ofile = open("file", 'w')
logging._ofile.write(data + '\n')
@APP.route('/dostuff')
@inlineCallbacks
def dostuff():
logging("before!")
os.system("command to upload the written file")
logging("after!")
return("42")
这里我们只打开一次日志记录文件,当_ofile未定义为日志记录属性时,第一次调用日志记录。在后续调用中,logging._ofile已经打开,try块中的write语句将成功。
例程dostuff()调用日志记录以指示我们即将完成工作,实际完成工作,然后调用日志记录以指示工作已完成,最后返回所需的值。