python multiprocessing多进程中开辟多线程的问题

叙述起来有些麻烦,大致说一下:主程序中我开辟了进程池,然后我在其中一个进程中开辟个线程池,利用该线程池并发请求页面!但是,我发现线程池的分配必须写到函数中,如果写到外边当作全局变量的话,程序就一直卡在那里,不知道是阻塞了?还是无限的“递归”下去了?总之不出结果。


虽然我知道,线程池不能在主程序中分配好然后作为参数传给某个进程中使用,毕竟是进程嘛。一个道理,import的时候其实全局变量的线程池已经分配好了,然后你在进程中调用应该跟刚才说的是一个道理。我不明白的是为什么会一直运行....为什么不是报错?
代码贴在下边:
segmentfault不支持上传文件么??只能大家copy代码执行了。


主程序:开辟进程池,其中某个进程调用另一个文件的抓取方法,将url列表作为参数:

import json
import urllib_fetcher as ulib_fetch
#from multiprocessing.dummy import Pool as ThreadPool
from multiprocessing import Pool as ProcessPool
 
g_url_list = [ 
        'http://segmentfault.com/q/1010000000742958',
        'http://segmentfault.com/q/1010000003793646',
        'http://segmentfault.com/q/1010000003757654',
        'http://tieba.baidu.com/f?kw=%C4%E1%C2%EA&fr=ala0&tpl=5',
        ]   
 
def main():
    process_pool = ProcessPool(3)
    ret = process_pool.apply_async(ulib_fetch.fetch, (g_url_list,))
    process_pool.close()
    process_pool.join()
    print json.dumps(ret.get(), False, False)
 
 
if __name__ == "__main__":
    main()

另一个抓取方法文件:urllib_fetcher.py

import sys
import urllib2 as ulib
from multiprocessing.dummy import Pool as ThreadPool
import socket
 
###########global var############
socket.setdefaulttimeout(5)  # 设置全局的超时时间
thread_pool = ThreadPool(5)  # ****全局线程池放到这里不行***
###########global var############
 
 
def unify_request(url):
    """
    统一请求,失败的请求线程将返回None
    """
    try:
        res = ulib.urlopen(url)
    except:
        sys.stderr.write("urllib_fetcher failed for url: %s\n" % url)
        return None
    result = { 
            "url" : res.url,
            "code" : res.code,
            }
    return result
 
 
def fetch(urls):
    # thread_pool = ThreadPool(5)  # 放到这里ok
    try:
        results = thread_pool.map(unify_request, urls)
    except:
        sys.stderr.write("fetch failed!\n")
        return []
 
    thread_pool.close() 
    thread_pool.join() 
    return results    
阅读 7.4k
1 个回答

多进程和多线程当然不一样,虽然 multiprocessing 为它们封装了形式基本一致的Pool接口,但Process和thread不能混为一谈。不同进程空间不共享变量,除非在共享内存上开辟。

全局的thread_pool、 g_url_list并不是一份,而是子进程从父进程fork出来后继承的一份独立副本, 在两个进程中各自修改并不互相影响。

但 fork 有个特性,在单线程状况复制内存数据到子进程,并不复制父进程的多线程状态。也就是说,即使子进程tread_pool对象有和父进程一样的数据,但不并存在ThreadPool(5)创建的5个线程。

至于为什么阻塞,我没看过multiprocessing的源码,猜想是根本等不到线程唤醒事件。

建议读APUE,注意thread and fork的章节,python的线程池进程池本质上都是对系统调用的封装组合。线程和进程没有像python里这些上层接口展现给你的这么简单,但如果从源码学习,反而C的代码更简单易理解。为了易用,multiprocessing 其实做了很多。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题