Linux Python、多线程、套接字有时无法创建

Linux Python、多线程、套接字有时无法创建,linux,python-2.7,sockets,python-multithreading,low-level,Linux,Python 2.7,Sockets,Python Multithreading,Low Level,最近观察到一种相当奇怪的行为,这种行为只发生在Linux中,而不发生在freeBSD中。我想知道是否有人对此有解释,或者至少有人猜测到底发生了什么 问题: 套接字创建方法,socket.socket(),有时会失败。只有当多个线程创建套接字时才会发生这种情况,单线程工作正常 要扩展socket.socket()失败,大多数时候我会看到“错误13:权限被拒绝”,但我也看到了“错误93:协议不受支持” 注: 我在Ubuntu18.04(有bug)和FreeBSD12.0(没有bug)上试过这个 仅当

最近观察到一种相当奇怪的行为,这种行为只发生在Linux中,而不发生在freeBSD中。我想知道是否有人对此有解释,或者至少有人猜测到底发生了什么

问题:

套接字创建方法,
socket.socket()
,有时会失败。只有当多个线程创建套接字时才会发生这种情况,单线程工作正常

要扩展
socket.socket()
失败,大多数时候我会看到“错误13:权限被拒绝”,但我也看到了“错误93:协议不受支持”

注:

  • 我在Ubuntu18.04(有bug)和FreeBSD12.0(没有bug)上试过这个
  • 仅当多个线程正在创建套接字时才会发生这种情况
  • 我使用UDP作为套接字的协议,尽管这似乎更具容错性。我也用TCP尝试过,它甚至会因为类似的错误而变得更快
  • 这只是偶尔发生的,所以可能需要多次运行,或者像我在下面提供的情况一样,大量的线程也可以做到这一点
  • 代码:

    下面是一些可以用来重现的最基本的代码:

    
    从线程导入线程
    导入套接字
    def foo():
    udp=socket.getprotobyname('udp')
    尝试:
    send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM,udp)
    例外情况除外,如e:
    打印类型(e)
    打印报告(e)
    def main():
    对于范围内的(6000):
    t=线程(目标=foo)
    t、 开始()
    main()
    
    注:

  • 我人为地使用了大量线程,只是为了最大限度地提高在使用UDP的运行中至少出现一次错误的概率。正如我前面所说的,如果您尝试TCP,您将看到大量线程数量的错误。但实际上,即使是更真实的线程数,比如20个甚至10个,也会触发错误,您可能只需要多次运行才能观察到错误
  • 使用while、try/except创建套接字将导致所有后续调用也失败
  • 用try/except包围套接字创建,并在“异常处理”位重新启动函数,即再次调用该函数将有效且不会失败
  • 欢迎提出任何意见、建议或解释

    附言


    从技术上讲,我知道我可以通过让一个线程创建我需要的尽可能多的套接字并将它们作为参数传递给我的其他线程来解决我的问题,但这并不是真正的重点。我更感兴趣的是为什么会发生这种情况以及如何解决它,而不是可能有什么变通办法,尽管这些办法也很受欢迎。:)

    我设法解决了它。问题来自
    getprotobyname()
    不是线程安全的

    见:

    另一方面,看看这些提示,这可能会导致并发问题,但是我的实验证明它不会,也许有人可以跟进

    无论如何,对于任何感兴趣的人来说,固定版本的代码都是在主线程中获取协议号(似乎是明智的,并且应该首先这样做),然后将其作为参数传递。它既可以减少您执行的系统调用,又可以修复程序中与并发相关的任何问题。代码如下所示:

    从线程导入线程
    导入套接字
    def foo(proto_num):
    尝试:
    send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM,proto_num)
    例外情况除外,如e:
    打印类型(e)
    打印报告(e)
    def main():
    proto_num=socket.getprotobyname('udp')
    对于范围内的(6000):
    t=Thread(target=foo,args=(proto_num,)
    t、 开始()
    main()
    
    以“权限被拒绝”或“协议不受支持”形式创建套接字的异常不会以这种方式报告。另外,请注意,如果使用SOCK_DGRAM,则proto_编号是多余的,可能会被完全跳过,但是,如果有人想要创建SOCK_原始套接字,则解决方案将更为相关