我想第一次在Python中使用并发。因此,我开始阅读有关Python并发性的大量文章(GIL,线程与进程,多处理与并发,未来与……),并看到了许多令人费解的示例。即使在使用高级并发功能库的示例中。
所以我决定开始尝试一些东西,并对最终得到的非常非常简单的代码感到惊讶:
from concurrent.futures import ThreadPoolExecutor
class WebHostChecker(object):
def __init__(self, websites):
self.webhosts = []
for website in websites:
self.webhosts.append(WebHost(website))
def __iter__(self):
return iter(self.webhosts)
def check_all(self):
# sequential:
#for webhost in self:
# webhost.check()
# threaded:
with ThreadPoolExecutor(max_workers=10) as executor:
executor.map(lambda webhost: webhost.check(), self.webhosts)
class WebHost(object):
def __init__(self, hostname):
self.hostname = hostname
def check(self):
print("Checking {}".format(self.hostname))
self.check_dns() # only modifies internal state, i.e.: sets self.dns
self.check_http() # only modifies internal status, i.e.: sets self.http
使用这些类如下:
webhostchecker = WebHostChecker(["urla.com", "urlb.com"])
webhostchecker.check_all() # -> this calls .check() on all WebHost instances in parallel
相关的多处理/线程代码仅 3行。我几乎不必修改现有代码(我希望能够在第一次开始编写用于顺序执行的代码时做到这一点,但是在在线阅读了许多示例后开始对此表示怀疑。)
然后...它有效! :)
它可以完美地在多个线程之间分配IO等待,并且运行时间少于原始程序的1/3。
所以,现在,我的问题:
任何能使我加深了解的见解/评论/评论/改进/ ...将不胜感激! :)
答案 0 :(得分:0)
好,所以我要添加自己的第一个陷阱:
如果webhost.check()引发异常,则线程仅结束,并且可能未设置self.dns和/或self.http。但是,使用当前代码,您将看不到异常,除非您还访问executor.map()结果!让我想知道为什么某些对象在运行check_all()后会引发AttributeErrors:
只需评估每个结果即可轻松解决此问题(始终为None,因为我不让.check()返回任何内容)。您可以在所有线程都已运行或运行期间执行此操作。我选择在此期间(即在with语句内)引发异常,因此程序在出现第一个意外错误时停止:
def check_all(self):
with ThreadPoolExecutor(max_workers=10) as executor:
# this alone works, but does not raise any exceptions from the threads:
#executor.map(lambda webhost: webhost.check(), self.webhosts)
for i in executor.map(lambda webhost: webhost.check(), self.webhosts):
pass
我想我也可以使用list(executor.map(lambda webhost:webhost.check(),self.webhosts)),但这会不必要地消耗内存。