从一个 url 出发,打印出所有链接出去的 url ,所有 url 只打印一次。
首先是单线程版本的,用 BFS ,同时用一个 set 记录访问过的 url 就可以了.
start = "http://google.com"
queue = [start]
visited = {start}
while queue:
url = queue.pop(0)
print url
for next_url in extract_urls(url):
if next_url not in visited:
queue.append(next_url)
visited.add(next_url)
然后要求把这个改成多线程,我是这样写的,不知道对不对:
class ThreadUrl(threading.Thread):
def __init__(self, queue, visited):
super(ThreadUrl, self).__init__()
self.queue = queue
self.visited = visited
def run(self):
while True:
url = self.queue.get()
print "%s: %s" % (self.name, url)
for next_url in extract_urls(url):
if next_url not in self.visited:
self.queue.put(next_url)
self.visited.add(next_url)
self.queue.task_done()
queue = Queue()
visited = set()
visited.add("http://google.com")
for i in range(5):
t = ThreadUrl(queue, visited)
t.setDaemon(True)
t.start()
queue.put(start_url)
queue.join()
没有学过操作系统,有些不确定。我的理解是, python 的Queue
是 thread safe 的,set
不是 thread safe 的。每次从 queue 里获取头部,这个是 thread safe 的,而我的if next_url not in self.visited
这条语句写在queue.get()
和queue.task_done()
之间,所以可以保证操作visited
也是 thread safe 的?因此我没有对visited
进行 synchronization...
如果我的思路是错的,那么我还需要 synchronization 。这种情况下是应该用 lock 吗?我这种 lock 的方法对吗?
class ThreadUrl(threading.Thread):
def __init__(self, queue, visited, lock):
super(ThreadUrl, self).__init__()
self.queue = queue
self.visited = visited
self.lock = lock
def run(self):
while True:
url = self.queue.get()
print "%s: %s" % (self.name, url)
for next_url in extract_urls(url):
self.lock.acquire()
if next_url not in self.visited:
self.queue.put(next_url)
self.visited.add(next_url)
self.lock.release()
self.queue.task_done()
最后,这题是一道比较开放式的题目,对于多线程的版本,是否有更优的解法,或者有哪些注意点值得跟面试官讨论呢?
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.