Python Twisted 中 LoopingCall 和 callInThread 的区别
我试图找出 Twisted 中的 task.LoopingCall 和reactor.callInThread 之间的差异。
LoopingCall 中的所有 self.sendLine 都会立即执行。 callInThread 中的则不是。它们仅在 LoopingCall 中的调用完成后才会发送。即使我发送了正确的分隔符。
这是为什么?有什么区别?它们不都是线程吗?
这是服务器:
from twisted.internet import reactor, protocol, task
from twisted.protocols import basic
from twisted.python import log
import sys
import time
import threading
import Queue
class ServerProtocol(basic.LineOnlyReceiver):
delimiter = '\0'
clientReady = 1
def __init__(self):
print 'New client has logged on. Waiting for initialization'
def lineReceived(self, line):
if line.startswith('I'):
print 'Data started with I: '+line
user = dict(uid=line[1:6], x=line[6:9], y=line[9:12])
self.factory.users[user['uid']] = user
log.msg(repr(self.factory.users))
self.startUpdateClient(user)
reactor.callInThread(self.transferToClient)
self.sendLine(user['uid'] + ' - Beginning - Initialized')
print user['uid'] + ' - Beginning - Initialized'
elif line.startswith('P'):
print 'Ping!'
elif line[0:3] == 'ACK':
print 'Received ACK'
self.clientReady = 1
#else:
#self.transport.loseConnection()
def _updateClient(self, user):
if self._running == 0:
self._looper.stop()
return
self._running -= 1
self._test += 1
print user['uid'] + ' Sending test data' + str(self._test)
self.sendLine(user['uid'] + ' Test Queue Data #%d' % (self._test,) + '\0')
def startUpdateClient(self, user):
self._running, self._test = 25, 0
self._looper = task.LoopingCall(self._updateClient, user)
self._looper.start(1, now=False)
print user['uid'] + ' - Startupdateclient'
def transferToClient(self):
test = 20
while test > 0:
if self.clientReady == 1:
test = test-1
print 'Reactor test ' + str(test) + ' - ' + str(time.time())
self.clientReady = 0
self.sendLine('This is reactortest ' + str(test) + ' - ' + str(time.time()) +' \0')
class Server(protocol.ServerFactory):
protocol = ServerProtocol
def __init__(self):
self.users = {}
if __name__ == '__main__':
log.startLogging(sys.stderr)
reactor.listenTCP(2000, Server())
reactor.run()
这是客户端:
#!/usr/bin/env python
import socket
import time
host = 'localhost'
port = 2000
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send('I12345070060\0')
running = 1
while running:
s.send('ACK\0')
data = s.recv(size)
if data:
print 'Received:', data
else:
print 'Closing'
s.close()
running=0
I'm trying to figure out the differences between a task.LoopingCall and a reactor.callInThread in Twisted.
All my self.sendLine's in the LoopingCall are performed immediately.
The ones in the callInThread are not. They're only sent after the one in the LoopingCall has finished. Even though I'm sending the right delimiter.
Why is that? What's the difference? Aren't they both threads?
This is the server:
from twisted.internet import reactor, protocol, task
from twisted.protocols import basic
from twisted.python import log
import sys
import time
import threading
import Queue
class ServerProtocol(basic.LineOnlyReceiver):
delimiter = '\0'
clientReady = 1
def __init__(self):
print 'New client has logged on. Waiting for initialization'
def lineReceived(self, line):
if line.startswith('I'):
print 'Data started with I: '+line
user = dict(uid=line[1:6], x=line[6:9], y=line[9:12])
self.factory.users[user['uid']] = user
log.msg(repr(self.factory.users))
self.startUpdateClient(user)
reactor.callInThread(self.transferToClient)
self.sendLine(user['uid'] + ' - Beginning - Initialized')
print user['uid'] + ' - Beginning - Initialized'
elif line.startswith('P'):
print 'Ping!'
elif line[0:3] == 'ACK':
print 'Received ACK'
self.clientReady = 1
#else:
#self.transport.loseConnection()
def _updateClient(self, user):
if self._running == 0:
self._looper.stop()
return
self._running -= 1
self._test += 1
print user['uid'] + ' Sending test data' + str(self._test)
self.sendLine(user['uid'] + ' Test Queue Data #%d' % (self._test,) + '\0')
def startUpdateClient(self, user):
self._running, self._test = 25, 0
self._looper = task.LoopingCall(self._updateClient, user)
self._looper.start(1, now=False)
print user['uid'] + ' - Startupdateclient'
def transferToClient(self):
test = 20
while test > 0:
if self.clientReady == 1:
test = test-1
print 'Reactor test ' + str(test) + ' - ' + str(time.time())
self.clientReady = 0
self.sendLine('This is reactortest ' + str(test) + ' - ' + str(time.time()) +' \0')
class Server(protocol.ServerFactory):
protocol = ServerProtocol
def __init__(self):
self.users = {}
if __name__ == '__main__':
log.startLogging(sys.stderr)
reactor.listenTCP(2000, Server())
reactor.run()
This is the client:
#!/usr/bin/env python
import socket
import time
host = 'localhost'
port = 2000
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send('I12345070060\0')
running = 1
while running:
s.send('ACK\0')
data = s.recv(size)
if data:
print 'Received:', data
else:
print 'Closing'
s.close()
running=0
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
否。
LoopingCall
使用callLater
;它在反应器中运行调用。是的,他们应该如此。
并不是说它们没有执行,而是因为您从线程调用了反应器 API,而您永远不允许这样做,因此您已将程序置于以下状态:< em>一切都彻底破碎了,永远。未来的每个 API 调用都可能会产生奇怪的、损坏的结果,或者没有结果,或者随机的、莫名其妙的崩溃。
您知道,多线程程序的正常工作方式;-)。
重复一遍:扭曲中的每个 API,唯一的例外是
callFromThread
(以及扩展调用callFromThread
的东西,例如blockingCallFromThread
),不是线程安全的。不幸的是,为每个 API 添加警告都会成为代码维护的噩梦,因此一些用户通过调用 API 并注意到一些奇怪的事情,以与您相同的方式发现了此限制。如果您有一些在需要调用反应器 API 的线程中运行的代码,请使用 callFromThread 或 blockingCallFromThread ,它会将调用分派到反应器线程,所有内容都应在该线程中工作顺利。然而,对于定时调用之类的东西,实际上根本不需要使用线程,而且它们会不必要地使您的程序复杂化。
No.
LoopingCall
usescallLater
; it runs the calls in the reactor.Yep, as they should be.
It's not so much that they're not performed, it's that because you called a reactor API from a thread, which you are never ever ever allowed to do, you have put your program into a state where everything is completely broken, forever. Every future API call may produce bizarre, broken results, or no results, or random, inexplicable crashes.
You know, the normal way multithreaded programs work ;-).
To repeat: every API in twisted, with the sole exception of
callFromThread
(and by extension things which callcallFromThread
, likeblockingCallFromThread
), is not thread safe. Unfortunately, putting in warnings for every single API would be both a code maintenance nightmare, so several users have discovered this constraint in the same way that you have, by calling an API and noticing something weird.If you have some code which runs in a thread that needs to call a reactor API, use
callFromThread
orblockingCallFromThread
and it will dispatch the call to the reactor thread, where everything should work smoothly. However, for stuff like timed calls, there's really no need to use threads at all, and they would needlessly complicate your program.您是否查看过 文档 <代码>循环调用?不涉及线程——它在主线程(通常是反应器的线程)上运行(每秒一次,以调用其
start
方法的方式)。 callInThread 是唯一的一个导致函数在单独的线程上运行的两个。Have you looked at the docs for
LoopingCall
? No thread involved -- it runs (every second, the way you're calling itsstart
method) on the main thread, i.e., typically, the thread of the reactor. callInThread is the only one of the two that causes the function to be run on a separate thread.