我刚刚使用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()
我不知道超时的确切时间,但在运行时会发生以下情况:
所以我唯一的猜测是这与GIL有关吗?显然,线程不会同时执行任务,一次只能尝试一个连接。
有没有人知道解决这个问题?
( asyncore 没有帮助,我现在不想使用 twisted ) 是不是可以用python完成这个简单的小事?
问候,汤姆
我在MacOSX上,我只是让我的朋友在linux上运行它,他确实得到了我希望获得的结果。即使在非线程环境中,他的socket.connects()也会立即返回。即使他将套接字设置为阻塞,并且超时为10秒,他的所有线程也会同时完成。
任何人都能解释一下吗?
答案 0 :(得分:15)
在某些系统上,getaddrinfo不是线程安全的。 Python认为,一些这样的系统是FreeBSD,OpenBSD,NetBSD,OSX和VMS。在这些系统上,Python专门为netdb维护一个锁(即getaddrinfo和朋友)。
因此,如果您无法切换操作系统,则必须使用其他(线程安全的)解析程序库,例如twisted。
答案 1 :(得分:2)
如果合适,您可以使用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())
答案 2 :(得分:1)
使用Twisted Names异步发送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()