2013-09-22 71 views
2

目前,我有一个url的列表来抓取内容,并正在做它连续。我想改变它并行抓住它们。这是一个伪代码。我想问的是设计的声音?我知道.start()启动线程,但是,我的数据库没有更新。我需要使用q.get()吗?谢谢帮助Python多线程

import threading  
import Queue 
q = Queue.Queue() 

def do_database(url): 
    """ grab url then input to database """ 
    webdata = grab_url(url) 
    try: 
     insert_data_into_database(webdata) 
    except: 
     .... 
    else: 
     < do I need to do anything with the queue after each db operation is done?> 

def put_queue(q, url): 
    q.put(do_database(url)) 

for myfiles in currentdir: 
    url = myfiles + some_other_string 
    t=threading.Thread(target=put_queue,args=(q,url)) 
    t.daemon=True 
    t.start() 
+0

确保您在'insert_data_into_database'函数中提交事务,否则不会写入任何内容。 –

+0

@Burhan Khalid,数据库工作正常,因为我提到我一直在串行。谢谢 – dorothy

回答

2

奇怪的是,你把东西放入q但从未取出任何东西出qq的目的是什么?另外,因为do_database()没有return任何东西,肯定看起来像q.put(do_database(url))确实是Noneq

这些工作的常用方式是将要做的工作描述添加到队列中,然后固定数量的线程轮流将事情从队列中拉出。你可能不希望创建线程;-)

无限多个这里有一个相当完整 - 但未经检验 - 素描:

import threading 
import Queue 

NUM_THREADS = 5 # whatever 

q = Queue.Queue() 
END_OF_DATA = object() # a unique object 

class Worker(threading.Thread): 
    def run(self): 
     while True: 
      url = q.get() 
      if url is END_OF_DATA: 
       break 
      webdata = grab_url(url) 
      try: 
       # Does your database support concurrent updates 
       # from multiple threads? If not, need to put 
       # this in a "with some_global_mutex:" block. 
       insert_data_into_database(webdata) 
      except: 
       #.... 

threads = [Worker() for _ in range(NUM_THREADS)] 
for t in threads: 
    t.start() 

for myfiles in currentdir: 
    url = myfiles + some_other_string 
    q.put(url) 

# Give each thread an END_OF_DATA marker. 
for _ in range(NUM_THREADS): 
    q.put(END_OF_DATA) 

# Shut down cleanly. `daemon` is way overused. 
for t in threads: 
    t.join() 
+0

嗨,q的目的,就是把每个任务都放进去。好的,所以我确实需要在完成每项任务后将其取出。感谢您的素描。会尝试去理解。 – dorothy

+0

好吧,** my **代码中的'q'包含您要获取的URL。在你的代码中,你正在调用**'do_database(url)',并将该调用的**结果**放入你的'q'中。有一个很大的区别;-) –

+0

是的,我明白了。我对put()作品略有困惑。 – dorothy

2

你应该用异步编程而不是线程来做到这一点。 Python中的线程化是有问题的(请参阅:Global Interpreter Lock),无论如何,您并不想在这里实现多核性能。您只需要一种方法来复用潜在的长时间运行的I/O。为此,您可以使用单个线程和事件驱动库,如Twisted

Twisted带有HTTP功能,因此您可以在结果进入时发出多个并发请求并作出反应(通过填充数据库)。请注意,此编程模型可能需要一点时间才能适应,但它会给您如果您所要求的请求数量不是天文数字(即,如果您可以在一台机器上完成所有操作,这似乎是您的意图),那么性能会很好。

+0

谢谢,我不认为我想安装另一个库,因为我想要做的事很简单。获取一堆网址并同时插入到数据库中,而不是一个接一个地循环。谢谢 – dorothy

1

对于DB,你必须提交之前更改生效。但是,为每个插入提交都不是最优的。批量更改后提交更好的性能。

对于并行,Python不是天生的。对于你的用例,我想用gevent使用python将是一个无痛的解决方案。

这里是一个更有效的伪实施FYI:

import gevent 
from gevent.monkey import patch_all 
patch_all() # to use with urllib, etc 
from gevent.queue import Queue 


def web_worker(q, url): 
    grab_something 
    q.push(result) 

def db_worker(q): 
    buf = [] 
    while True: 
    buf.append(q.get()) 
    if len(buf) > 20: 
     insert_stuff_in_buf_to_db 
     db_commit 
     buf = [] 

def run(urls): 
    q = Queue() 
    gevent.spawn(db_worker, q) 
    for url in urls: 
    gevent.spawn(web_worker, q, url) 


run(urls) 

加,因为这是实现完全单线程的,你可以放心地操作工人像队列,数据库连接,全局变量等

之间共享数据