Python Interpreter блокирует многопоточные DNS-запросы? - PullRequest
8 голосов
/ 31 июля 2009

Я немного поигрался с питоном и потоками и понял, что даже в многопоточном скрипте 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. Потоки заканчиваются последовательно, не все сразу (время ожидания = одинаково для всех!)

Так что мое единственное предположение, что это имеет отношение к GIL? Очевидно, что потоки не выполняют свою задачу одновременно, одновременно выполняется только одно соединение.

Кто-нибудь знает способ обойти это?

( asyncore не помогает, и я бы предпочел не использовать twisted сейчас) Разве нельзя сделать эту простую маленькую вещь с помощью Python?

Привет, Том

редактирование:

Я нахожусь на MacOSX, я просто позволил моему другу запустить это на linux, и он действительно получил результаты, которые я хотел получить. Его socket.connects () немедленно возвращается, даже в не поточной среде. И даже когда он устанавливает сокеты на блокировку и тайм-аут на 10 секунд, все его потоки заканчиваются одновременно.

Может кто-нибудь объяснить это?

Ответы [ 3 ]

15 голосов
/ 31 июля 2009

В некоторых системах getaddrinfo не является потокобезопасным. Python считает, что такими системами являются FreeBSD, OpenBSD, NetBSD, OSX и VMS. В этих системах Python поддерживает блокировку специально для netdb (т. Е. Getaddrinfo и друзья).

Поэтому, если вы не можете переключать операционные системы, вам придется использовать другую (поточно-ориентированную) библиотеку распознавателей, например, витую.

2 голосов
/ 05 августа 2009

если это подходит, вы можете использовать модуль 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())
1 голос
/ 18 ноября 2010

Отправка DNS-запросов асинхронно с использованием Искаженные имена :

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...