好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Python学习第十二课(线程池)

对于任务数量比较多,而且不断增加的程序,每一个任务生成一个线程,这样频繁的操作,会让线程数量失控,同时有些线程的生成和销毁都需要cpu和数据资源,另外,线程死锁也是很大问题,为了更好的管理线程,于是开发了线程池模块。例如:爬虫程序遇到的问题。python中的threadpool模块,逐步被python3中的标准库concurrent.futures模块提供的ThreadPoolExecutor和ProcessPoolExecutor两个类替代。这两个类对threading和multiprocessing做了进一步封装和抽象。 一、ThreadPoolExecutor 1、submit方法: 这个类提供了submit来提交一个线程给线程池。

 from concurrent.futures import ThreadPoolExecutor
import time

# 参数times用来模拟网络请求的时间
def get_html(times):
    time.sleep(times)
    print("get page {}s finished".format(times))
    return times

executor = ThreadPoolExecutor(max_workers=2)
# 通过submit函数提交执行的函数到线程池中,submit函数立即返回,不阻塞
task1 = executor.submit(get_html, (3))
task2 = executor.submit(get_html, (2))
# done方法用于判定某个任务是否完成
print(task1.done())
# cancel方法用于取消某个任务,该任务没有放入线程池中才能取消成功
print(task2.cancel())
time.sleep(4)
print(task1.done())
# result方法可以获取task的执行结果
print(task1.result())

# 执行结果
# False  # 表明task1未执行完成
# False  # 表明task2取消失败,因为已经放入了线程池中
# get page 2s finished
# get page 3s finished
# True  # 由于在get page 3s finished之后才打印,所以此时task1必然完成了
# 3     # 得到task1的任务返回值 

max_workers表示线程池最大线程数量。 submit方法提交任务线程给线程池并且返回任务句柄,submit不是阻塞调用,会立刻返回句柄。 cancel方法可以取消提交的任务,如果已经在执行,则无法取消。 result方法获取返回值,这个方法是阻塞的,因为它要等待线程执行完毕才能得到结果。

2、 as_completed 这个方法是一个generator,在某个线程完成之后,使用yield获取结果,如果没有,则阻塞,一直到循环到任务结束。这是个协程的概念。

 from concurrent.futures import ThreadPoolExecutor, as_completed
import time

# 参数times用来模拟网络请求的时间
def get_html(times):
    time.sleep(times)
    print("get page {}s finished".format(times))
    return times

executor = ThreadPoolExecutor(max_workers=2)
urls = [3, 2, 4] # 并不是真的url
all_task = [executor.submit(get_html, (url)) for url in urls]

for future in as_completed(all_task):
    data = future.result()
    print("in main: get page {}s success".format(data))

# 执行结果
# get page 2s finished
# in main: get page 2s success
# get page 3s finished
# in main: get page 3s success
# get page 4s finished
# in main: get page 4s success 

3、map executor.map方法,也可以取出结果。使用map方法的时,不用submit。例如:

 from concurrent.futures import ThreadPoolExecutor
import time

# 参数times用来模拟网络请求的时间
def get_html(times):
    time.sleep(times)
    print("get page {}s finished".format(times))
    return times

executor = ThreadPoolExecutor(max_workers=2)
urls = [3, 2, 4] # 并不是真的url

for data in executor.map(get_html, urls):
    print("in main: get page {}s success".format(data))
# 执行结果
# get page 2s finished
# get page 3s finished
# in main: get page 3s success
# in main: get page 2s success
# get page 4s finished
# in main: get page 4s success 

注意,这里的输出结果的顺序和map的顺序相同。

3、wait方法 wait可以让主线程阻塞,知道满足要求后,继续执行。他接受3个参数,等待任务序列、超时时间和等待条件,时间上类似event的功能。

 from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED, FIRST_COMPLETED
import time

# 参数times用来模拟网络请求的时间
def get_html(times):
    time.sleep(times)
    print("get page {}s finished".format(times))
    return times

executor = ThreadPoolExecutor(max_workers=2)
urls = [3, 2, 4] # 并不是真的url
all_task = [executor.submit(get_html, (url)) for url in urls]
wait(all_task, return_when=ALL_COMPLETED)
print("main")
# 执行结果 
# get page 2s finished
# get page 3s finished
# get page 4s finished
# main 

二、总结 python把线程池和进程池放到future模块里。future这个设计,在很多脚本语言里都有,初步接触会麻烦点。future对象也是异步执行的核心。

查看更多关于Python学习第十二课(线程池)的详细内容...

  阅读:49次