【问题标题】:Multi-threaded Python server that exits when threads finish线程完成时退出的多线程 Python 服务器
【发布时间】:2012-06-14 14:17:15
【问题描述】:

假设您有一个相当基本的客户端/服务器代码,其中每个客户端创建三个线程并且多个客户端可以同时连接。我希望服务器等待传入连接,一旦它开始获取连接,就运行直到没有更多线程在运行,然后退出。代码类似于下面。 (即,而不是服务器“永远服务”,我希望它在所有线程完成后退出)。

编辑:我希望服务器等待传入连接。一旦连接开始,它应该继续接受连接,直到没有线程运行,然后退出。这些联系会有些零散。

import socket
import threading

# Our thread class:
class ClientThread ( threading.Thread ):

   # Override Thread's __init__ method to accept the parameters needed:
   def __init__ ( self, channel, details ):

      self.channel = channel
      self.details = details
      threading.Thread.__init__ ( self )

   def run ( self ):

      print 'Received connection:', self.details [ 0 ]
      self.channel.send ( 'hello from server' )
      for x in xrange ( 10 ):
         print self.channel.recv ( 1024 )
      self.channel.close()
      print 'Closed connection:', self.details [ 0 ]

# Set up the server:
server = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
server.bind ( ( '', 2727 ) )
server.listen ( 5 )

# Have the server serve "forever":
while True:
   channel, details = server.accept()
   ClientThread ( channel, details ).start()

【问题讨论】:

  • 如果服务器一直在服务,它应该如何知道在决定关闭之前它应该预期多少个连接?如果立即有 3 个连接进来,然后什么都没有,服务器可能会开始等待那个 3。但是可能会进来第四个。你是说从第一个连接进来的那一刻起,你希望服务器检测到没有更多客户端线程处于活动状态和关闭的瞬间?它总是在等待一个 100% 空闲的时刻死掉?
  • 是的,确切地说,一旦空闲它就会死掉,但在此之前它应该等到第一个或两个连接进来。

标签: python multithreading sockets client


【解决方案1】:

根据您的 cmets,您正在寻找的是在第一个连接到服务器后开始计算连接,并在没有更多现有连接时终止服务器。

当前无限 while 循环的一个直接问题是每个 accept() 上的块。所以无论如何,它总是在等待另一个连接。您将不得不从其他线程中断它以使其脱离该循环。但另一种解决方案是让你的事件循环更大,接受新连接的行为只是其中的一部分。循环还应该检查退出条件。

这个例子只是一种可能的方式。它使用Queue.Queue 来协调工作计数器。

import socket
import threading
import select
from Queue import Queue

class ClientThread ( threading.Thread ):

   def __init__ ( self, channel, details, queue=None ):
      self.channel = channel
      self.details = details
      self.queue = queue
      threading.Thread.__init__ ( self )

   def run ( self ):

      if self.queue:
         self.queue.put(1)

      print 'Received connection:', self.details [ 0 ]
      self.channel.send ( 'hello from server' )
      for x in xrange ( 10 ):
         print self.channel.recv ( 1024 )
      self.channel.close()
      print 'Closed connection:', self.details [ 0 ]

      if self.queue:
         self.queue.get_nowait()

# Set up the server:
server = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
server.bind ( ( '', 2727 ) )
server.listen ( 5 )

rlist = [server]
work_queue = Queue()

def accept_client():
   channel, details = server.accept()
   ClientThread ( channel, details, work_queue ).start() 

accept_client()

while not work_queue.empty():
   server_ready, _, _ = select.select(rlist,[],[], .25)
   if server in server_ready:
      accept_client()

print "Shutting down"
server.close()
print "Exiting"

我们使用select.select 作为一种简单的方法来检测服务器套接字上的活动,但也有超时。如果服务器准备好了,那么我们接受新的连接。如果它达到.25 秒超时,我们只需再次循环并等待。

您会看到我们创建了一个队列并不断检查它是否为空。队列被传递到每个线程。当一个线程启动时,它会将一些工作记录到队列中。数据是任意的。只是一面旗帜。当线程完成时,它将从队列中清除该项目。结果是收到第一个连接后,队列不再为空,循环会一直运行。如果在任何时候队列变为空(因为所有当前线程都已完成),循环将中断,服务器将关闭。

【讨论】:

  • 哇,谢谢!看起来我有一些工作要做,我觉得我应该使用选择和队列模块,但我不知道从哪里开始。
  • @COpython:是的,就像我说的,这只是一种方法。您可以通过不使用队列的方式完成此操作。 select.select 非常便携,因此也很有帮助。
【解决方案2】:

如果你从while循环中break,进程会一直等到所有ClientThreads退出,然后才会终止。

这会起作用,因为客户端线程是非守护进程。 请参阅threading.Thread.daemon 了解更多详情。

【讨论】:

  • 嗯,试过这个,但是在完成第一个连接的客户端后它就中断了。即,这只有在所有线程同时连接时才有效。
  • @COpython:这意味着当主循环停止时只有一个客户端线程。我认为您需要明确您希望该过程何时停止的要求。
  • 我相信您认为服务器在收到连接后会中断,我正在寻找的是它在线程完成后退出。
  • @COpython:不,在所有(非守护进程)线程完成之前,进程不会退出。
  • 但它同时执行每个线程,所以假设线程 A 和 B 连接,然后 A 完成,打破循环,然后线程 C 想要在 B 完成之前连接。我得到的行为是线程 C 无法连接,因为 A 已经打破了循环..?
猜你喜欢
  • 1970-01-01
  • 2012-11-20
  • 1970-01-01
  • 1970-01-01
  • 2014-03-17
  • 2018-01-23
  • 2013-02-11
  • 1970-01-01
相关资源
最近更新 更多