Python中两个类之间的线程不正常
我写了两个.py文件,用来让它们之间互相通信。 A.py 监听8888端口,并向7777端口发送数据; B.py 监听7777端口,并向8888端口发送数据。 这两个客户端在启动服务器后都陷入了无限循环, 问题出在哪里呢? 如果我只在A.py中使用服务器,而在B.py中使用客户端(反过来也一样),不使用任何线程,它们就能正常工作。
A.py:
import socket
import threading
import thread
import time
class server(threading.Thread):
s = ''
host = 0
port = 0
def __init__(self):
threading.Thread.__init__(self)
global s,host,port
s = socket.socket()
host = socket.gethostname()
port = 8888
def run(self):
global s,host,port
print 'Server started!'
print 'Waiting for clients...'
s.bind((host, port))
s.listen(5)
c, addr = s.accept()
print 'Got connection from', addr
while True:
time.sleep(2)
msg = c.recv(1024)
if len(msg)==0 : break
print addr, ' >> ', msg
class client(threading.Thread):
s = ''
host = 0
port = 0
def __init__(self):
threading.Thread.__init__(self)
global s,host,port
s = socket.socket()
host = socket.gethostname()
port = 7777
def run(self):
try:
time.sleep(5)
global s,host,port
print 'Connecting to ', host, port
s.connect((host, port))
print "Connectd"
while True:
time.sleep(2)
msg = raw_input('CLIENT >> ')
if len(msg)==0:break
s.send(msg)
except:
print "Waiting"
self.run()
thread1 = server()
thread2 = client();
thread1.start()
thread2.start()
thread1.join()
thread2.join();
B.py:
import socket
import threading
import thread
import time
class server(threading.Thread):
s = ''
host = 0
port = 0
def __init__(self):
threading.Thread.__init__(self)
global s,host,port
s = socket.socket()
host = socket.gethostname()
port = 7777
def run(self):
global s,host,port
print 'Server started!'
print 'Waiting for clients...'
s.bind((host, port))
s.listen(5)
c, addr = s.accept()
print 'Got connection from', addr
while True:
time.sleep(2)
msg = c.recv(1024)
if len(msg)==0 : break
print addr, ' >> ', msg
class client(threading.Thread):
s = ''
host = 0
port = 0
def __init__(self):
threading.Thread.__init__(self)
global s,host,port
s = socket.socket()
host = socket.gethostname()
port = 8888
def run(self):
try:
time.sleep(5)
global s,host,port
print 'Connecting to ', host, port
s.connect((host, port))
print "connected"
while True:
time.sleep(2)
msg = raw_input('CLIENT >> ')
if len(msg)==0:break
s.send(msg)
except:
print "waiting"
self.run();
thread1 = server()
thread2 = client();
thread1.start()
thread2.start()
thread1.join()
thread2.join();
2 个回答
我觉得你的问题部分是因为Python的全局解释器锁造成的。这个锁限制了CPython解释器只能在一个线程上执行字节码。也就是说,即使你的脚本使用了多个线程,实际上一次只能有一个线程在运行。你的程序之所以卡住,是因为你的服务器实例在等待输入时被阻塞了,这样它就不会释放全局解释器锁,导致客户端无法发送数据。
不过,幸运的是,有几个解决办法:
- 使用Python的多进程包,让你的程序使用进程而不是线程。因为你已经在通过TCP套接字在类之间共享数据,这样改动代码的工作量应该很小。
- IronPython和Jython在实现中没有使用全局解释器锁,所以如果你坚持想用线程而不是进程,可以考虑一下这两个项目。
如果你感兴趣的话,David Beazley几年前做过一个关于全局解释器锁的有趣的演讲。
使用
global s, host, port
是问题的根源。比如在 A.py 中,服务器和客户端的类都在修改同样的变量s
、host
和port
。如果把端口改成相同的值,就会搞乱服务器或客户端(哪个先运行就会出问题)。如果没有必要,千万不要用
global
,而且大多数情况下你真的不需要。在这种情况下,你可以通过使用实例属性来解决问题。另外,我建议在编写
client.run
方法时,不要使用递归调用self.run()
。Python 对递归调用的次数是有限制的,如果客户端等待的时间太长,递归调用可能会导致你的程序崩溃。相反,你可以使用while
循环来实现这个功能。(见下文)。
import argparse
import socket
import threading
import thread
import time
class server(threading.Thread):
def __init__(self, port):
threading.Thread.__init__(self)
self.s = socket.socket()
self.host = socket.gethostname()
self.port = port
def run(self):
print 'Server started!'
print 'Waiting for clients...'
self.s.bind((self.host, self.port))
self.s.listen(5)
c, addr = self.s.accept()
print 'Got connection from', addr
while True:
time.sleep(2)
msg = c.recv(1024)
if len(msg) == 0 : break
print addr, ' >> ', msg
class client(threading.Thread):
def __init__(self, port):
threading.Thread.__init__(self)
self.s = socket.socket()
self.host = socket.gethostname()
self.port = port
def run(self):
while True:
time.sleep(5)
print 'Connecting to ', self.host, self.port
try:
self.s.connect((self.host, self.port))
break
except Exception as err:
print "Waiting", err
print "Connectd"
while True:
time.sleep(2)
msg = raw_input('CLIENT >> ')
if len(msg) == 0:break
self.s.send(msg)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--server_port', type = int, default = 8888)
parser.add_argument('--client_port', type = int, default = 7777)
args = parser.parse_args()
thread1 = server(args.server_port)
thread2 = client(args.client_port)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
用以下命令运行它:
test.py --server 8888 --client 7777
test.py --server 7777 --client 8888