Twisted框架管理两种线程:一种是主线程,一种是辅助线程。主线程只有一个,即reactor.run()运行的线程;辅助线程可以有多个,以线程池的方式呈现。
Twisted定义的事件类函数都在主线程中运行。
为了提高运行效率,TWisted框架中大多数内置函数都不是线程安全的(例如:Protocol.transport.write()函数),因此需要将这些内置函数放入到主线程中执行,否则可能导致逻辑错误或者系统崩溃。
使代码在主线程中运行
reactor.callFromThread(func,*args,**kw):可以使代码运行在主线程中;func参数代表要执行的函数
# coding:utf8
# 示例代码(不一定能运行)
from twisted.internet.protocol import Protocol
from twisted.internet import reactor
class MyProtocol(Protocol):
pass
protocol = MyProtocol()
def must_run_in_main_thread(message):
protocol.send=True
protocol.transport.write(message)
def run_in_any_thread():
reactor.callFromThread(must_run_in_main_thread,'message to...')
print('the run of must_run_in_main_thread() has been finished')
使代码在辅助线程中运行
在主线程中遇到较耗时的处理时,可以使用reactor.callInThread()函数建立辅助线程
如果辅助线程中处理的数据需要调用主线的函数,可以使用reactor.callFromThread()函数将处理结果传递到主线程
辅助线程执行结果可以通过reactor.callFromThread将数据返回到主线程处理
# coding:utf8
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
import time
def log_operation(msg):
time.sleep(10)
print('got message before 10 seconds :%s'%(msg,))
reactor.callFromThread(protocol.transport.write(message.encode('utf8')))
class Echo(DatagramProtocol):
def datagramReceived(self, datagram: bytes, addr):
str_msg = atagram.decode('utf8')
reactor.callInThread(log_operation,str_msg)
protocol = Echo()
port = 8009
reactor.listenUDP(port,protocol)
reactor.run()
配置线程池
可以使用reactor.suggestThreadPoolsize()函数定义线程池中的线程数量
reactor.suggestThreadPoolsize(10) # 辅助线程数量为10
子线程返回数据获取(Defer)
twisted.internet.threads.deferToThread(func,*args,**kw) 创建Defer对象d,开启辅助线程
d.addCallback(func)添加回调函数,用于接收子线程返回的数据
from twisted.internet import reactor, threads
def doLongCalculation(x):
# .... do long calculation here ...
return 3*9
def printResult(x):
print(x)
# run method in thread and get result as defer.Deferred
d = threads.deferToThread(doLongCalculation,9)
d.addCallback(printResult)
reactor.run()
主线程中接收子线程返回的数据
twisted.internet.threads.blockingCallFromThread(reactor,f,*args,**kw)启动新线程,并等待线程返回结果
twisted.web.client.getPage(url:bytes)获取html页面源代码
下例中使用 twisted.web.client.getPage获取网页,并在子线程中获取返回结,reactor.callInThread()在辅助线程中执行(一共开启3个线程,一个主线程,一个辅助线程,一个获取网页的辅助线程)
from twisted.internet import threads, reactor, defer
from twisted.web.client import getPage
from twisted.web.error import Error
def inThread():
try:
# 开启线程
result = threads.blockingCallFromThread(reactor, getPage,
"https://www.baidu.com/".encode('utf8'))
except Error as exc:
print(exc)
else:
print (result.decode('utf8'))
reactor.callFromThread(reactor.stop)
reactor.callInThread(inThread) # 开启线程
reactor.run()