4

总体概述:

我正在尝试构建一个使用线程从网络上抓取信息的应用程序的简单示例。我知道有专门的模块(例如,scrapy),但我想做更多的事情来学习和理解线程是如何工作的,并理解其中的陷阱。此外,我还看到了各种教程(IBM 教程和其他教程)、关于 SO 的问题,甚至Python Cookbook 第 3 版中的一些食谱。描述了如何做到这一点,但在使用线程/队列时我仍然被挂断。

首先,我在 stackoverflow 上(以及Cookbook中)读到 subclass 是一种浪费threading.Thread,所以我一直在尝试使用Thread(target=..., args=(...)). 大多数教程似乎都使用(旧的?)子类化方法,我可能会因为采用这种新的(?)方式而感到困惑。

其次,在Python Cookbook中,他们提供了将_sentinel值放入工作队列的示例,您的方法会注意并在发现它时退出。我认为这是我遇到麻烦的地方。

最后,我确信我必须参与各种矫枉过正的事情,所以我会很感激关于我在哪里使事情过于复杂的提示或指示。感谢您的建议。


具体的编码问题和尝试:

我有一个 (company name, url) 元组的 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

谢谢阅读。

4

1 回答 1

3

问题是你正在加入work_queue,但work_queue不是空的——它仍然有哨兵。试试这个:

t1.join()
t2.join()
于 2013-08-04T22:58:57.650 回答