一般概述:
我正在尝试构建一个应用程序的简单示例,该应用程序使用线程从 Web 中抓取信息。我知道有专门的模块(例如,scrapy),但我想更多地学习和理解线程的工作原理并了解陷阱。此外,我还看过各种教程(IBM 教程和其他教程)、关于 SO 的问题,甚至是 Python Cookbook,第 3 版中的一些食谱。描述了如何做到这一点,但在使用线程/队列时我仍然在某个地方挂断电话。
首先,我在 stackoverflow 上(以及在 Cookbook 中)读到,将 threading.Thread
子类化是一种浪费,所以我一直在尝试使用 Thread(target=..., args=(...))
来做到这一点。大多数教程似乎使用(较旧的?)子类化方法,我可能会因为采用这种较新的(?)方式而感到困惑。
其次,在 Python Cookbook 中,他们提供了将 _sentinel
值放入工作队列的示例,您的方法会注意并在发现它时中断。我想这是我遇到麻烦的地方。
最后,我确信我必须从事各种矫枉过正的事情,所以我会很感激有关我将事情过度复杂化的提示或指示。感谢您的建议。
具体编码问题和尝试:
我有一个(公司名称,网址)元组的 work_queue。我的方法是解析股票代码名称的 url,并将元组(代码名称、公司名称)发送到输出队列。当所有值都被消耗时,我试图使用哨兵来打破该方法:
def grab_ticker_name(input_q, output_q, _sentinel):
'''Function to be threaded later: grabs company ticker from url and sends it to an output queue.'''
while True:
item = input_q.get()
if item is _sentinel:
print("We encountered the sentinel!!")
input_q.put(_sentinel) # See page 492 of Python Cookbook, 3rd Ed.
break
else:
company_name, company_url = item
with urlopen(company_url) as f:
newsoup = BeautifulSoup(f)
if len(newsoup.select('.inlink_chart > a')) > 0:
ticker_name = newsoup.select('.inlink_chart > a')[0].string
output_q.put((ticker_name, company_name))
print("grab_ticker_name grabbed the following: {ticker_name}, {company_name}".format_map(vars()))
input_q.task_done()
我正在填充队列并像这样构建线程(使用
print
语句只是为了帮助我了解发生了什么):def main():
work_queue = queue.Queue()
out_queue = queue.Queue()
_sentinel = object() # This recommendation comes from the *Python Cookbook*
stock_list = StockBuilder()
print("Populating the work_queue now")
for item in stock_list.yield_url():
work_queue.put(item)
work_queue.put(_sentinel)
t1 = threading.Thread(target=grab_ticker_name, args=(work_queue, out_queue, _sentinel))
t2 = threading.Thread(target=grab_ticker_name, args=(work_queue, out_queue, _sentinel))
print("Now we're going to start our threads!")
t1.start() ; t2.start()
print("Now we're going to attempt to join our work queue.")
work_queue.join()
它似乎工作并给我一行一行的值,但随后似乎无限循环,直到我必须取消它(CTRL + C):
...
grab_ticker_name grabbed the following: MRK, Merk
grab_ticker_name grabbed the following: HUM, Humana
We encountered the sentinel!!
grab_ticker_name grabbed the following: WLP, WellPoint
We encountered the sentinel!!
^CTraceback (most recent call last).
...
问题 :为什么
break
表达式没有使函数返回 main()?最后,我还希望能够从
out_queue
中获取所有数据并将其放入另一个数据结构(列表、字典等)中。问题 2: 我知道我可以为此使用带锁的列表,但我在这里也很困惑,因为我认为我读过队列是线程修改可变对象的更简单方法?
问题 2a :假设我使用 out_queue,一旦填充了
out_queue
,是否有一种简单的方法可以简单地从中获取所有值?我想也许我会在 _sentinel
之后将另一个 out_queue
放入 work_queue.join()
,然后再执行另一个 while
循环并处理 out_queue
中的每个项目(如果它不是 _sentinel
)?这是一种天真的方法吗?谢谢阅读。
最佳答案
问题是您正在加入 work_queue
,但 work_queue
不为空——它仍然有哨兵。试试这个:
t1.join()
t2.join()