Python Twisted 中 LoopingCall 和 callInThread 的区别

ske*_*rit 1 python sockets multithreading twisted

我试图找出 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()

Run Code Online (Sandbox Code Playgroud)

这是客户:


#!/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

Run Code Online (Sandbox Code Playgroud)

Gly*_*yph 5

这是为什么?有什么不同?它们不都是线程吗?

编号 LoopingCall用途callLater;它在反应器中运行调用。

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

是的,他们应该如此。

callInThread 中的则不是。

并不是说它们没有被执行,而是因为你从一个线程调用了一个reactor API,而你永远不允许这样做,你已经将你的程序置于一切都完全崩溃的状态,永远。未来的每个 API 调用都可能会产生奇怪的、损坏的结果,或者没有结果,或者随机的、莫名其妙的崩溃。

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

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

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