Python中两个类之间的线程不正常

1 投票
2 回答
1819 浏览
提问于 2025-04-17 12:27

我写了两个.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 个回答

0

我觉得你的问题部分是因为Python的全局解释器锁造成的。这个锁限制了CPython解释器只能在一个线程上执行字节码。也就是说,即使你的脚本使用了多个线程,实际上一次只能有一个线程在运行。你的程序之所以卡住,是因为你的服务器实例在等待输入时被阻塞了,这样它就不会释放全局解释器锁,导致客户端无法发送数据。

不过,幸运的是,有几个解决办法:
- 使用Python的多进程包,让你的程序使用进程而不是线程。因为你已经在通过TCP套接字在类之间共享数据,这样改动代码的工作量应该很小。
- IronPythonJython在实现中没有使用全局解释器锁,所以如果你坚持想用线程而不是进程,可以考虑一下这两个项目。

如果你感兴趣的话,David Beazley几年前做过一个关于全局解释器锁的有趣的演讲

2
  • 使用 global s, host, port 是问题的根源。比如在 A.py 中,服务器和客户端的类都在修改样的变量 shostport。如果把端口改成相同的值,就会搞乱服务器或客户端(哪个先运行就会出问题)。

    如果没有必要,千万不要用 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

撰写回答