python:如何在多线程中发送数据包然后线程自杀

时间:2009-03-03 03:37:31

标签: python multithreading sockets timer packet

我有一个问题。我想使用python向一些主机发送连续的字节流一段时间(假设1分钟)。

到目前为止,这是我的代码:

#! /usr/bin/env python                                                          

import socket
import thread
import time

IP = "192.168.0.2"
PADDING = "a" * 1000 #assume the MTU is slighly above 1000
DATA = PADDING + "this is sentence number = "
PORT = 14444
killed = False
test_time = 60 #60 seconds of testing

def send_data():
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((IP, PORT))
  count = 1
  starttime = time.clock()
  while elapsed < test_time:
    sent = s.send(DATA + str(count) + "\n")
    if sent == 0: break # assume that if nothing is sent -> connection died
    count = count+1
    elapsed = time.clock() - starttime
    if killed:
      break
  s.close()
  print str(count) + " has been sent"

print "to quit type quit"
thread.start_new_thread(send_data, ())

while True:
  var = raw_input("Enter something: ")
  if var == "quit":
    killed = True

很少有问题,除了每次轮询time.clock之外,有没有更好的方法让线程在60秒后死亡? 当我运行这个程序时,它正确地发送字节,但是当我键入退出时,另一个线程不会死,即使我设置了var killed = True。我想知道为什么会这样? var Killed的范围应该到达另一个线程吗?

由于

7 个答案:

答案 0 :(得分:5)

我推荐使用线程模块。更有利的是使用InterruptableThread来终止线程。您不必使用标志来终止您的线程,但如果您从父线程调用此线程上的terminate(),则会发生异常。你可以处理异常。

import threading, ctypes

class InterruptableThread(threading.Thread):
@classmethod
def _async_raise(cls, tid, excobj):
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
    if res == 0:
        raise ValueError("nonexistent thread id")
    elif res > 1:
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")

def raise_exc(self, excobj):
    assert self.isAlive(), "thread must be started"
    for tid, tobj in threading._active.items():
        if tobj is self:
            self._async_raise(tid, excobj)
            return

def terminate(self):
    self.raise_exc(SystemExit)

修改 您可以使用另一个等待1分钟然后杀死其他线程的线程重写您的代码

def send_data:
    IP = ...
    # other vars

    ...
    s = socket.socket(.....)

    # no killed checking
    # no time checking
    # just do your work here
    ...
    s.close()


my_thread = InterruptableThread(target=send_data)
my_thread.start()

def one_minute_kill(who):
   time.sleep(60)
   who.terminate()

killer_thread = InterruptableThread(target=one_minute_kill, args=[my_thread])
killer.start()

print "to quit type quit"
while my_thread.isAlive():
  if raw_input("Enter something: ") == "quit":
    my_thread.terminate()

答案 1 :(得分:2)

我不知道如何使用“thread”模块执行此操作,但我可以使用“threading”模块执行此操作。我认为这段代码可以实现你想要的。

有关线程模块的文档: http://docs.python.org/library/threading.html

#!/usr/bin/python

import time
from threading import Thread
import threading
import sys

test_time = 10
killed = False

class SillyThread( threading.Thread ):
    def run(self):
        global killed
        starttime = time.time()
        counter = 0
        while (time.time() - starttime) < test_time:
            if killed:
                break
            counter = counter + 1
            time.sleep(0.1)
        print "I did %d loops" % counter

class ManageThread( threading.Thread ):
    def run(self):
        global killed
        while True:
            var = raw_input("Enter something: ")
            if var == "quit":
                killed = True
                break
        print "Got var [%s]" % var

silly = SillyThread()
silly.start()
ManageThread().start()
Thread.join(silly)
print "bye bye"
sys.exit(0)

请注意,我使用time.time()而不是time.clock()。 time.clock()给出了Unix上经过的处理器时间(参见http://docs.python.org/library/time.html)。我认为time.clock()应该无处不在。我将test_time设置为10秒,因为我没有耐心一分钟。

如果我让它运行整整10秒,会发生什么:

leif@peacock:~/tmp$ ./test.py
Enter something: I did 100 loops
bye bye

如果我输入'quit',会发生什么:

leif@peacock:~/tmp$ ./test.py
Enter something: quit
Got var [quit]
I did 10 loops
bye bye

希望这有帮助。

答案 2 :(得分:1)

如上所述,使用threading模块,它更容易使用,并提供了几个同步原语。它还提供了一个Timer类,它在指定的时间后运行。

如果您只想让程序退出,您可以简单地将发送线程作为守护程序。您可以在调用start()之前调用setDaemon(True)来执行此操作(2.6可能会使用守护程序属性)。只要非守护程序线程正在运行,Python就不会退出。

答案 3 :(得分:1)

没有线程,你可以很容易地做到这一点。例如,使用Twisted,您只需设置定时调用和生产者:

from twisted.internet.protocol import ClientFactory, Protocol
from twisted.internet import reactor

class Noisy(Protocol):
    def __init__(self, delay, data):
        self.delay = delay
        self.data = data

    def stop(self):
        self.transport.unregisterProducer()
        self.transport.loseConnection()
        reactor.stop()

    def resumeProducing(self):
        self.transport.write(self.data)

    def connectionMade(self):
        self.transport.registerProducer(self, False)
        reactor.callLater(self.delay, self.stop)

factory = ClientFactory()
factory.protocol = lambda: Noisy(60, "hello server")
reactor.connectTCP(host, port, factory)
reactor.run()

与线程方法相比,这有许多优点。它不依赖于守护程序线程,因此您实际上可以清理网络连接(例如,在必要时发送关闭消息),而不是依靠平台来销毁它。它为您处理所有实际的低级网络代码(在socket.send返回0的情况下,您的原始示例正在做错事;此代码将正确处理该情况)。您也不必依赖ctypes或模糊的CPython API来在另一个线程中引发异常(因此它可以移植到更多版本的Python并且实际上可以立即中断阻塞的发送,这与其他一些建议的方法不同)。 / p>

答案 4 :(得分:0)

确保“退出”正常工作并添加一个小字体以测试输入是否正常。

if var == "quit":
 print "Hey we got quit"

答案 5 :(得分:0)

未初始化已过去的变量。在while循环上方将其设置为零。

答案 6 :(得分:0)

测试killed

的范围很容易
>>> import thread
>>> killed = False
>>> import time
>>> def test():
...  while True:
...   time.sleep(1)
...   if killed:
...     print 'Dead.'
...     break
... 
>>> thread.start_new_thread(test, ())
25479680
>>> time.sleep(3)
>>> killed = True
>>> Dead.