代码之家  ›  专栏  ›  技术社区  ›  Tom

python解释器是否阻止多线程DNS请求?

  •  8
  • Tom  · 技术社区  · 17 年前

    我只是对python和线程进行了一些处理,并意识到即使在多线程脚本中,DNS请求也会阻塞。请考虑以下脚本:

    从线程导入线程 导入插座

    class Connection(Thread):
        def __init__(self, name, url):
            Thread.__init__(self)
            self._url = url
            self._name = name
    
        def run(self):
            print "Connecting...", self._name
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.setblocking(0)
                s.connect((self._url, 80))
            except socket.gaierror:
                pass #not interested in it
            print "finished", self._name
    
    
    if __name__ == '__main__':
        conns = []
        # all invalid addresses to see how they fail / check times
        conns.append(Connection("conn1", "www.2eg11erdhrtj.com"))
        conns.append(Connection("conn2", "www.e2ger2dh2rtj.com"))
        conns.append(Connection("conn3", "www.eg2de3rh1rtj.com"))
        conns.append(Connection("conn4", "www.ege2rh4rd1tj.com"))
        conns.append(Connection("conn5", "www.ege52drhrtj1.com"))
    
        for conn in conns:
            conn.start()
    

    我不知道超时的确切时间,但是当运行这个时,会发生以下情况:

    1. 所有的线程都启动了,我得到了我的打印输出
    2. 每隔XX秒,一个线程显示完成,而不是一次显示全部。
    3. 线程按顺序完成,而不是一次全部完成(超时=全部相同!)

    所以我唯一的猜测是这和吉尔有关?显然,线程不会同时执行它们的任务,一次只尝试一个连接。

    有人知道怎么解决这个问题吗?

    ( 异步内核 没用,我不想用 扭曲的 眼下) 难道不可能用Python完成这个简单的小事情吗?

    问候,汤姆

    编辑:

    我在Macosx上,我只是让我的朋友在Linux上运行这个,他确实得到了我想要的结果。即使在非线程环境中,他的socket.connects()也会立即返回。即使当他将套接字设置为阻塞,并将超时设置为10秒时,他的所有线程也会同时完成。

    有人能解释一下吗?

    3 回复  |  直到 14 年前
        1
  •  15
  •   Martin v. Löwis    17 年前

    在某些系统上,getaddrinfo不是线程安全的。python相信一些这样的系统是freebsd、openbsd、netbsd、osx和vms。在这些系统中,python专门为netdb(即getaddrinfo和friends)维护一个锁。

    因此,如果不能切换操作系统,就必须使用不同的(线程安全)解析器库,如Twisted。

        2
  •  2
  •   m1k3y3    14 年前

    如果合适的话,你可以使用 multiprocessing 启用基于进程的并行性的模块

    import multiprocessing, socket
    
    NUM_PROCESSES = 5
    
    def get_url(url):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.setblocking(0)
            s.connect((url, 80))
        except socket.gaierror:
            pass #not interested in it
        return 'finished ' + url
    
    
    def main(url_list):
        pool = multiprocessing.Pool( NUM_PROCESSES )
        for output in pool.imap_unordered(get_url, url_list):
            print output
    
    if __name__=="__main__":
        main("""
                 www.2eg11erdhrtj.com 
                 www.e2ger2dh2rtj.com
                 www.eg2de3rh1rtj.com
                 www.ege2rh4rd1tj.com
                 www.ege52drhrtj1.com
              """.split())
    
        3
  •  1
  •   jfs    15 年前

    使用异步发送DNS请求 Twisted Names :

    import sys
    from twisted.internet import reactor
    from twisted.internet import defer
    from twisted.names    import client
    from twisted.python   import log
    
    def process_names(names):
        log.startLogging(sys.stderr, setStdout=False)
    
        def print_results(results):
            for name, (success, result) in zip(names, results):
                if success:
                    print "%s -> %s" % (name, result)
                else:
                    print >>sys.stderr, "error: %s failed. Reason: %s" % (
                        name, result)
    
        d = defer.DeferredList(map(client.getHostByName, names), consumeErrors=True)
        d.addCallback(print_results)
        d.addErrback(defer.logError)
        d.addBoth(lambda _: reactor.stop())
    
    reactor.callWhenRunning(process_names, """
        google.com
        www.2eg11erdhrtj.com 
        www.e2ger2dh2rtj.com
        www.eg2de3rh1rtj.com
        www.ege2rh4rd1tj.com
        www.ege52drhrtj1.com
        """.split())
    reactor.run()