Python扭曲中的LoopingCall和callInThread之间的区别

时间:2010-07-10 22:03:34

标签: python sockets multithreading twisted

我试图找出task.LoopingCall和Twisted中的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

2 个答案:

答案 0 :(得分:4)

  

为什么?有什么不同?他们不是两个线程吗?

没有。 LoopingCall使用callLater;它在反应堆中运行呼叫。

  

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

是的,他们应该如此。

  

callInThread中的那些不是。

并不是因为它们没有被执行,而是因为你从一个线程调用了一个反应堆API,而你从来没有这样做过,你已经将你的程序放入了状态一切都完全被打破,永远。每个未来的API调用都可能产生奇怪的,破碎的结果,或者没有结果,或者随机的,无法解释的崩溃。

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

要重复:除了callFromThread(以及调用callFromThread的扩展内容,如blockingCallFromThread)之外,每个API都是扭曲的,不是线程安全的< / em>的。不幸的是,为每个API添加警告都是代码维护的噩梦,因此有几个用户通过调用API并注意到一些奇怪的东西,以与您相同的方式发现了这种约束。

如果您有一些代码在需要调用reactor API的线程中运行,请使用callFromThreadblockingCallFromThread,它会将调用分派给reactor线程,其中一切都应该顺利进行。但是,对于像定时调用这样的东西,根本不需要使用线程,它们会不必要地使你的程序复杂化。

答案 1 :(得分:1)

你看过LoopingCall的{​​{3}}了吗?没有线程涉及 - 它在主线程上运行(每秒一次,就像你调用它的start方法一样),即通常是反应器的线程。 docs是导致函数在单独线程上运行的两个中唯一的一个。