Python Twisted 中 LoopingCall 和 callInThread 的区别

发布于 2024-09-09 02:57:07 字数 2912 浏览 4 评论 0原文

我试图找出 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

少女情怀诗 2024-09-16 02:57:07

这是为什么呢?有什么区别?它们不是都是线程吗?

否。 LoopingCall 使用 callLater;它在反应器中运行调用。

LoopingCall 中的所有 self.sendLine 都会立即执行。

是的,他们应该如此。

callInThread 中的不是。

并不是说它们没有执行,而是因为您从线程调用了反应器 API,而您永远不允许这样做,因此您已将程序置于以下状态:< em>一切都彻底破碎了,永远。未来的每个 API 调用都可能会产生奇怪的、损坏的结果,或者没有结果,或者随机的、莫名其妙的崩溃。

您知道,多线程程序的正常工作方式;-)。

重复一遍:扭曲中的每个 API,唯一的例外是 callFromThread (以及扩展调用 callFromThread 的东西,例如 blockingCallFromThread),不是线程安全的。不幸的是,为每个 API 添加警告都会成为代码维护的噩梦,因此一些用户通过调用 API 并注意到一些奇怪的事情,以与您相同的方式发现了此限制。

如果您有一些在需要调用反应器 API 的线程中运行的代码,请使用 callFromThread 或 blockingCallFromThread ,它会将调用分派到反应器线程,所有内容都应在该线程中工作顺利。然而,对于定时调用之类的东西,实际上根本不需要使用线程,而且它们会不必要地使您的程序复杂化。

Why is that? What's the difference? Aren't they both threads?

No. LoopingCall uses callLater; it runs the calls in the reactor.

All my self.sendLine's in the LoopingCall are performed immediately.

Yep, as they should be.

The ones in the callInThread are not.

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 call callFromThread, like blockingCallFromThread), 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 or blockingCallFromThread 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.

扶醉桌前 2024-09-16 02:57:07

您是否查看过 文档 <代码>循环调用?不涉及线程——它在主线程(通常是反应器的线程)上运行(每秒一次,以调用其 start 方法的方式)。 callInThread 是唯一的一个导致函数在单独的线程上运行的两个。

Have you looked at the docs for LoopingCall? No thread involved -- it runs (every second, the way you're calling its start 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文