Разница между LoopingCall и callInThread в витой Python - PullRequest
1 голос
/ 11 июля 2010

Я пытаюсь выяснить разницу между задачей. LoopingCall и реактор.

Все мои 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

Ответы [ 2 ]

4 голосов
/ 11 июля 2010

Почему это?Какая разница?Разве они не темы?

Нет.LoopingCall использует callLater;он запускает вызовы в реакторе.

Все мои self.sendLine в LoopingCall выполняются немедленно.

Да, как и должно быть.

Те, что в callInThread не являются.

Это не так много, что они не выполняются, это потому, что вы вызвали реактор API из потока, который вы никогда не позволяя делать , вы переводите свою программу в состояние, в котором все полностью сломано, навсегда .Каждый будущий вызов API может привести к причудливым, ошибочным результатам или отсутствию результатов, или случайным необъяснимым сбоям.

Знаете, нормальный способ работы многопоточных программ; -).

Повторять: каждыйAPI в витой, за единственным исключением callFromThread (и, соответственно, вещи, которые вызывают callFromThread, такие как blockingCallFromThread), не безопасен для потоков .К сожалению, вставка предупреждений для каждого отдельного API была бы кошмаром для обслуживания кода, поэтому несколько пользователей обнаружили это ограничение так же, как вы, вызвав API и заметив что-то странное.

Если у вас естьнекоторый код, который выполняется в потоке, который должен вызывать API реактора, использует callFromThread или blockingCallFromThread, и он будет отправлять вызов в поток реактора, где все должно работать гладко.Однако для таких вещей, как синхронизированные вызовы, совсем не нужно использовать потоки, и они без необходимости усложнят вашу программу.

1 голос
/ 11 июля 2010

Вы смотрели на документы для LoopingCall?Поток не задействован - он запускается (каждую секунду, как вы вызываете его метод start) в основном потоке, т. Е. Обычно в потоке реактора. callInThread - единственный из двух, который вызывает запуск функции в отдельном потоке.

...