【问题标题】:Multi threaded application is not outputting properly多线程应用程序未正确输出
【发布时间】:2017-08-15 20:04:08
【问题描述】:

我最近被介绍到 python 中的线程模块,所以我决定尝试一下我在端口 7000 上打开了一个 python 套接字服务器:

import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',7000))
s.listen(1)
c, a = s.accept()

并让我的客户端服务器尝试连接从 1 到 65535 的每个端口,直到它在端口 7000 上建立连接。显然这需要很长时间,所以我对其进行了多线程处理:

import threading
import socket
import sys
host = None
def conn(port):
    try:
        s.connect((host,port))
        print 'Connected'
        sys.exit(1)
    except:
        pass
    global host
host = '127.0.0.1'
for i in range(65535):
    t = threading.Thread(target=conn, args=(i,))
    t.start()

当客户端连接时,它假设返回消息“已连接”但是在调试时我注意到程序有一些非常奇怪的行为。有时程序会返回它已连接,有时程序会无法输出它已连接到服务器,而是会终止而不打印任何内容。

这显然是线程的问题。当我让客户端连接到端口 7000 时,它只有 100% 的时间工作。然而,通过所有 65535 端口将其线程化会导致客户端有时不打印任何内容。这是什么原因,我该如何预防或规避它。

编辑: 我意识到让它尝试连接到较少数量的端口,端口 1-10 和端口 7000,使它有更高的打印输出连接的机会。

【问题讨论】:

  • 启动 65535 个线程可能不是解决这个问题的好方法。更好的方法可能是生成更少的线程,每个线程都尝试连接到端口子集。无论如何,您可能应该在循环中添加一个检查,以便在建立连接后停止尝试连接/生成新线程。拥有大量线程可能会使您的程序有点不稳定/不可预测。
  • 另外,你有没有在你创建的线程上join(参见here)?如果您只是生成它们然后退出,那么您可能在它们能够连接之前就退出了。这可以解释行为的差异,因为它归结为处理器调度等。
  • 在当前编辑中,def conn(port): 似乎在递归调用自身。
  • 做一个 thread.join() 会使线程化的目的无效我想让这个过程尽可能快
  • 不会的。创建和运行线程,存储引用,然后将它们加入一个单独的循环中。

标签: python multithreading sockets


【解决方案1】:

如果connect() 失败,则将套接字的状态视为未指定。便携式应用程序应关闭套接字并创建一个新的套接字以重新连接。

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.connect(('127.0.0.1', 6999))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 61] Connection refused
>>>
>>> s.connect(('127.0.0.1', 7000))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 22] Invalid argument
>>> 
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.connect(('127.0.0.1', 7000))
# Connect success.

【讨论】:

    【解决方案2】:

    65535 是一个巨大的数字。

    您可能获得的任何性能提升都将与这么多线程相比相形见绌。操作系统应该为每个线程计划处理器时间,然后在线程之间切换需要时间。在最坏的情况下(7k 非常糟糕),所有操作系统所做的只是线程切换,而两者之间几乎没有真正的工作。 2-8 个线程(或者每个物理核心只有一个线程)会更好。

    另外,请确保您等到线程退出,并且不要使用except: pass 消除错误。我敢打赌,那里发生了很多有趣的事情。至少 [有选择地] 在某处记录这些异常。

    编辑。使用join 以确保所有衍生线程在主线程之前退出。

    threads = [threading.Thread(target=conn, args=(i,)) for i in range(8)]
    for thread in threads:
        thread.start()
    
    # do whatever
    
    for thread in threads:
        thread.join()
    

    【讨论】:

    • 我已经尝试过了,但我得到的行为仍然是一样的,线程似乎无法正确输出,所以它不是源于线程过早退出,降低了端口数量允许线程正确输出所以还有其他原因吗?
    • 你是否仍然使用except: pass 使所有异常保持沉默?
    • 是的,仍然有 65000 个线程,但是异常没有被静音
    • 所以我是否应该一次只运行几个线程,比如 3 到 8 个,直到我完成所有 65k 以最大限度地提高线程效率?
    猜你喜欢
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 2017-08-07
    • 2018-02-09
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    相关资源
    最近更新 更多