线程池

线程池的创建 - concurrent

concurrent 是 Python 的内置包,使用它可以帮助我们完成创建线程池的任务。

方法名 介绍 示例
futures.ThreadPoolExecutor 创建线程池 tpool=ThreadPoolExecutor(max_workers)

通过调用 concurrent 包的 futures 模块的 ThreadPoolExecutor 类,通过实例化 ThreadPoolExecutor 实现创建线程池的对象,它有一个参数来设置 线程池的数量。这和创建进程池设置的数量是完全相同的。

线程池的常用方法

接下里看一下线程池对象中都有哪些常用的方法 :

函数名 介绍 用法
submit 往线程池中添加任务 submit(target, args)
done 确认线程池中的某个线程是否完成了任务 done()
rsult 获取当前线程执行任务的结果 result()
  • submit 函数:通过 submit 函数将参数传入;该函数传入的参数也是传入要执行的函数与该函数的参数,由于它的参数并不用需要通过赋值语句的形式传入,只需要把相应的值传入就可以了(稍后会进行一个练习)。
  • done 函数:判断当前线程是否执行完成;返回值是 bool 类型。
  • result 函数:返回当前线程池中线程任务的执行结果,通过这种方法就可以获取线程池的返回值了。

线程池演示案例

1、定义一个函数实现循环的效果

2、定义一个线程池,设置线程的数量

# coding:utf-8


import time
from concurrent.futures import ThreadPoolExecutor


def work(i):
    print('第 {} 次循环'.format(i))
    time.sleep(1)   # 之所以每次都要使用 sleep 函数,是因为函数执行太快;通过 sleep 尝试模拟一下长时间的执行一个任务


if __name__ == '__main__':
    thread_poor = ThreadPoolExecutor(4)	# 实例化一个线程池,设置线程数量为4

    for i in range(20):
        thread_poor.submit(work, (i,))		# 利用 submit 函数将任务添加至 work 函数

运行效果如下 

从运行结果来看,我们的线程任务每次执行4个任务,阻塞一秒后再执行后续的四个线程的任务,是没有问题的。

PS:需要注意的是,运行结果有可能是出现将两个或者多个任务的结果在同一行打印输出,这是因为在同一时间处理了多个线程的任务,这也叫 "并发"。

线程锁

前文的进程池是与进程锁相对应匹配的,同样的线程池也有与之对应的 线程锁 。线程锁的使用方法几乎与进程锁是一样的,只不过线程锁对应的是线程罢了。

1.实例化一个线程锁

2、在 work 函数中调用线程锁

3、并获取 线程 的返回值(线程池也是可以获取返回值的)

代码示例如下:

 # coding:utf-8


import os
import time
import threading
from concurrent.futures import ThreadPoolExecutor


lock = threading.Lock()     # 全局定义一个 Lock() 实例

def work(i):
    lock.acquire()          # 区别于 进程锁 只需要在全局实例化一个即可,线程锁需要在线程任务的函数中调用 线程锁 才会生效
    print('当前是第 {} 次循环'.format(i))
    time.sleep(1)           # 之所以每次都要使用 sleep 函数,是因为函数执行太快;通过 sleep 尝试模拟一下长时间的执行一个任务
    lock.release()
    return '第 {} 次循环的进程id为:{}'.format(i, os.getpid())    # 线程也是基于进程实现的


if __name__ == '__main__':
    thread_poor = ThreadPoolExecutor(4)    # 实例化一个线程池,设置线程数量为4

    result = []

    for i in range(20):
        result_thread = thread_poor.submit(work, (i,))      # 利用 submit 函数将任务添加至 work 函数;
                                                            # 需要注意的是这里不像进程池那样使用赋值的形式传入 work 函数
        result.append(result_thread)

    for res in result:
        print(res.result())

运行结果如下:

从运行结果可以看到,之前一同执行的4个任务现在变成了一次只执行一个任务;每一个个线程都是在主进程 93215下执行的,说明线程与进程还是有所区别的,虽然我们有多个线程任务在执行,但是依然是在主进程下去完成的;同时我们还获取到了 线程的返回值 第 {} 次循环的进程id为:{}'.format(i, os.getpid() 。

以上就是线程池的使用和常用方法,我们会发现线程池的使用实际上要比进程池的使用要容易一些。进程池我们需要考虑 join 与 close 等一些问题,但是线程池则不需要那么的严格,并且线程相对于进程要更加的轻量,使用起来也更加的便捷。

利用线程池实现抽奖小案例

案例代码如下:

# coding:utf-8

import threading
import random
from concurrent.futures import ThreadPoolExecutor

lock = threading.Lock()
def luck_draw(arg):
    lock.acquire()
    # 从手机列表中随机选出一个中奖手机,其他手机均未中奖
    phone = random.choice(arg[0])
    # 在从奖池中随机选取一个奖品,视为该手机抽中的奖品
    price = random.choice(arg[1])
    prices.remove(price)
    phones.remove(phone)
    lock.release()
    return '恭喜手机尾号为{}的用户,抽到{}'.format(str(phone)[-5:-1], price)


if __name__ == '__main__':
    t = ThreadPoolExecutor(3)       # 通过创建三个线程从而实现每个线程完成一项抽奖任务
    # 确定抽奖人数
    phone_num = int(input('请输入抽奖的用户人数:'))
    # 模拟产生出相应数量的手机号
    phones = random.sample(range(13300000000, 19999999999), phone_num)      # 这里设置的随机号码仅做演示效果
    prices = ['一等奖:iPhone12 ProMax', '二等奖:ipad2021pro', '三等奖:air wetter']
    result = []
    for i in range(3): # 三个任务,每个线程分配一个
        t_result = t.submit(luck_draw, (phones, prices))
        result.append(t_result)

    for res in result:
        print(res.result())

运行效果如下:

GIL全局锁

本章节的开头我们就说过,该部分没有代码的相关练习。仅仅是对 GIL全局锁 做一个概念上的简单启蒙。

其他语言的线程与Python线程的区别

多线程与多进程的使用其实是比较复杂的,目前作为初学者来说涉及的还比较浅。最近的几个章节介绍了 进程与线程在CPU的执行方式,这里再进行拓展一下。

下面我们看一张图:

依然是一个CPU 与4个核心(可以认为是4条跑道);

先看左边的两条跑道,是进程1创建的3个线程。这三条线程有一个去了 1core 跑道,另外两条则去了 2core 跑道。线程之间有选择性的进入了不同的跑道,当然进程1的主进程或者说是主线程可能会在 1core 跑道、也可能会在 2core 跑道,这是其他语言进行多线程的样子。

再来看看右边,Python 创建的进程2。当进程创建之后,包含主线程一共产生了3个线程,而这三个线程都跑到了 4core 跑道 上去。它不会像其他语言那样去寻找不同的有空闲资源的跑道去执行,而是仅仅在主进程所处的跑道去执行线程。造成 Python 中的多线程无法在多条跑道执行任务的主要原因就是因为 GIL全局锁 ,这个 GIL 并不是 Python语法中添加上去的,而是 python解释器 在执行的时候自动加了这把 "锁" 。

GIL 的作用

因为 GIL 锁 的关系,使得 Python 的多线程无法在多个CPU跑道上去执行任务,它只能在单一CPU上进行工作。

这也限制了多线程的性能,毕竟 Python 的多线程只能在一条跑道上运行。跑道满了,运行速度依然会慢。而在多个跑道上运行的任务必然是要比单一跑道效率会高很多。

Python创始人 Guido 之所以保留 GIL 锁,其实也是为了线程之间的安全。虽然这个话题一直都在争论,不过我们也有办法去掉这个 GIL全局锁。

默认的解释器是 Python 自带的解释器,这里我们可以选择一个叫做 pypy 的解释器。通过它来执行 Python 脚本是不含有 GIL全局锁 的,但并不太推荐这种做法。

另外一种解决方法就是使用 多进程 多线程 的方式 来弥补这一短板上的问题,通过多进程在每个 CPU 跑道上执行任务,并且每个进程的跑道上再去执行多个线程。 ,让它们在各自的时间片上去运行。这些用法会在后续的章节会介绍到,在此只需要了解即可。

以上就是Python学习之线程池与GIL全局锁详解的详细内容,更多关于Python线程池 GIL全局锁的资料请关注Devmax其它相关文章!

Python学习之线程池与GIL全局锁详解的更多相关文章

  1. XCode 3.2 Ruby和Python模板

    在xcode3.2下,我的ObjectiveCPython/Ruby项目仍然可以打开更新和编译,但是你无法创建新项目.鉴于xcode3.2中缺少ruby和python的所有痕迹(即创建项目并添加新的ruby/python文件),是否有一种简单的方法可以再次安装模板?我发现了一些关于将它们复制到某个文件夹的信息,但我似乎无法让它工作,我怀疑文件夹的位置已经改变为3.2.解决方法3.2中的应用程序模板

  2. Swift基本使用-函数和闭包(三)

    声明函数和其他脚本语言有相似的地方,比较明显的地方是声明函数的关键字swift也出现了Python中的组元,可以通过一个组元返回多个值。传递可变参数,函数以数组的形式获取参数swift中函数可以嵌套,被嵌套的函数可以访问外部函数的变量。可以通过函数的潜逃来重构过长或者太复杂的函数。

  3. 10 个Python中Pip的使用技巧分享

    众所周知,pip 可以安装、更新、卸载 Python 的第三方库,非常方便。本文小编为大家总结了Python中Pip的使用技巧,需要的可以参考一下

  4. Swift、Go、Julia与R能否挑战 Python 的王者地位

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 红薯因 Swift 重写开源中国失败,貌似欲改用 Python

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  6. 你没看错:Swift可以直接调用Python函数库

    上周Perfect又推出了新一轮服务器端Swift增强函数库:Perfect-Python。对,你没看错,在服务器端Swift其实可以轻松从其他语种的函数库中直接拿来调用,不需要修改任何内容。以如下python脚本为例:Perfect-Python可以用下列方法封装并调用以上函数,您所需要注意的仅仅是其函数名称以及参数。

  7. Swift中的列表解析

    在Swift中完成这个的最简单的方法是什么?我在寻找类似的东西:从Swift2.x开始,有一些与你的Python样式列表解析相当的东西。(在这个意义上,它更像是Python的xrange。如果你想保持集合懒惰一路通过,只是这样说:与Python中的列表解析语法不同,Swift中的这些操作遵循与其他操作相同的语法。

  8. swift抛出终端的python错误

    每当我尝试启动与python相关的swift时,我都会收到错误.我该如何解决?

  9. 在Android上用Java嵌入Python

    解决方法看看this,它适用于J2SE,你可以尝试在Android上运行.

  10. Android – 线程池策略和Loader可以用来实现吗?

    )>如果不是什么是创建AsyncTask池的好方法,是否可能正在实施它?

随机推荐

  1. 10 个Python中Pip的使用技巧分享

    众所周知,pip 可以安装、更新、卸载 Python 的第三方库,非常方便。本文小编为大家总结了Python中Pip的使用技巧,需要的可以参考一下

  2. python数学建模之三大模型与十大常用算法详情

    这篇文章主要介绍了python数学建模之三大模型与十大常用算法详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感想取得小伙伴可以参考一下

  3. Python爬取奶茶店数据分析哪家最好喝以及性价比

    这篇文章主要介绍了用Python告诉你奶茶哪家最好喝性价比最高,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

  4. 使用pyinstaller打包.exe文件的详细教程

    PyInstaller是一个跨平台的Python应用打包工具,能够把 Python 脚本及其所在的 Python 解释器打包成可执行文件,下面这篇文章主要给大家介绍了关于使用pyinstaller打包.exe文件的相关资料,需要的朋友可以参考下

  5. 基于Python实现射击小游戏的制作

    这篇文章主要介绍了如何利用Python制作一个自己专属的第一人称射击小游戏,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起动手试一试

  6. Python list append方法之给列表追加元素

    这篇文章主要介绍了Python list append方法如何给列表追加元素,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  7. Pytest+Request+Allure+Jenkins实现接口自动化

    这篇文章介绍了Pytest+Request+Allure+Jenkins实现接口自动化的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  8. 利用python实现简单的情感分析实例教程

    商品评论挖掘、电影推荐、股市预测……情感分析大有用武之地,下面这篇文章主要给大家介绍了关于利用python实现简单的情感分析的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

  9. 利用Python上传日志并监控告警的方法详解

    这篇文章将详细为大家介绍如何通过阿里云日志服务搭建一套通过Python上传日志、配置日志告警的监控服务,感兴趣的小伙伴可以了解一下

  10. Pycharm中运行程序在Python console中执行,不是直接Run问题

    这篇文章主要介绍了Pycharm中运行程序在Python console中执行,不是直接Run问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

返回
顶部