2010-07-10 50 views
1

我想弄清楚Twisted中的task.LoopingCall和reactor.callInThread之間的差異。Python的Twisted中的LoopingCall和callInThread之間的差異

我的所有self.sendLine在LoopingCall中立即執行。 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 

+0

順便說一句,你的例子''import threading''和'import Queue',這是不必要的。 – Glyph 2010-07-11 03:15:17

+0

是的,那是因爲我有一個單獨的線程使用了一個隊列,但我刪除了這個問題。我忘了取消進口。 – skerit 2010-07-12 09:28:50

回答

4

這是爲什麼?有什麼不同?他們不是兩個線程?

LoopingCall使用callLater;它在反應堆中運行調用。

我的所有self.sendLine在LoopingCall中立即執行。

是的,因爲他們應該是。

callInThread中的那些不是。

它與其說他們不執行,那就是因爲你所謂的從一個線程反應器API,你是永遠不允許做,你已經把你的程序進入狀態一切都完全破碎,永遠。每個將來的API調用都可能會產生奇怪的,破壞的結果,或者沒有結果,或者隨機的,無法解釋的崩潰。

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

重複:每個API的扭曲,與callFromThread(其中要求callFromThread,像blockingCallFromThread並通過擴展的東西)是唯一的例外,是不是線程安全。不幸的是,爲每個API添加警告都會成爲代碼維護的噩夢,所以有幾個用戶通過調用一個API並注意到一些奇怪的東西,像您一樣發現了這個約束。

如果您有一些代碼需要調用反應器API,請使用callFromThreadblockingCallFromThread,它會將調用發送到反應器線程,其中一切都應該順利進行。但是,對於定時調用等內容,實際上根本不需要使用線程,而且它們會不必要地使程序複雜化。

+0

這是我第一次使用Python。我第一次使用線程,你能告訴嗎? ;) 我會重新思考我的策略! – skerit 2010-07-12 09:32:30

1

你看了docsLoopingCall?不涉及任何線程 - 它在主線程(即通常是反應器的線程)上運行(每秒鐘,您調用方法start的方法)。 callInThread是導致函數在單獨線程上運行的唯一兩個函數。

+0

Ow,我一直在看Core文檔,而不是API參考。但是爲什麼線程的sendLine不像循環中的那樣工作? – skerit 2010-07-11 01:29:51

+2

您的代碼中的錯誤是,您直接從非主線程調用可調用的函數,除非是從主線程調用 - 只要您需要調用扭曲的API條目,請使用'callFromThread'從非主線程! – 2010-07-11 04:00:56