Python入门教学——多进程和多线程

2023-10-26

目录

一、线程和进程

1、线程和进程的基本概念

2、线程和进程的关系

3、串行、并行和并发

二、创建多个线程

1、线程相关的模块

2、创建线程

2.1、通过Thread类构造器来创建新线程

2.2、通过继承于Thread类来创建新线程

三、锁

1.1、锁的概念

1.2、GIL锁

1.3、如何加锁

四、线程间通讯

1.1、消息队列

1.2、常用接口

1.3、演示

五、创建多个进程

1、以指定函数作为参数创建进程

2、继承Process类创建进程

六、进程间通讯

七、总结

1、多线程和多进程的优缺点

2、使用场景


一、线程和进程

1、线程和进程的基本概念

  • 进程是资源分配的最小单位,线程是CPU调度的最小单位。
  • 进程:相当于是公司的老板,他自己本身不干活,但为干活的员工提供资源、平台。
  • 线程:相当于是公司的员工,真正干活的人就是这些员工。

2、线程和进程的关系

  • 一个线程只能属于一个进程,一个进程可以有多个进程,每个进程至少有一个线程(主线程)。
  • 资源分配给进程,一个进程中的所有线程将共享该进程中所有资源。
  • 线程在执行过程中,需要协作同步,不同进程间的线程需要利用消息向步机制来实现通讯。
  • 真正执行任务的是线程,它是进程内可调度的实体。
  • 创建进程要比创建线程消耗更多的计算机资源。
  • 进程间不会相互影响,而相同进程内部如果有一个线程出现异常,则其他所有线程将全部被阻塞。
  • 同一进程内不同线程使用同一个资源时需要加锁。

3、串行、并行和并发

  • 串行:做完一件事再完成另一件事。完成任务时间是所有单个任务完成时间的总和。
  • 并行:两个或多个事件在同一时刻发生,相当于多个人同时完成多件事,是物理意义上的并行。
  • 并发:两个或多个事件在同一时间间隔内发生相当于一个人同时做多件事,是逻辑意义上的并行。

二、创建多个线程

1、线程相关的模块

  • _thread:该模块是在python3之前thread模块的重命名,它是比较底层的模块,所以一般不会在代码中直接使用。
  • threading:python3 之后的线程模块,编程中一般都使用这个模块来创建线程。

2、创建线程

2.1、通过Thread类构造器来创建新线程
  • 使用threading.Thread直接生成一个线程,并且定义一个函数传给target,其中包含了该线程要完成的任务。
  • 创建两个线程,分别去访问百度和搜狐的网站。
    • import threading
      import urllib.request
      # 访问网站
      def get_web(url):  # 访问网站
              user_agent = '在从自己的浏览器里找'
              headers = {
                      "User-Agent": user_agent
              }
              # 构造Request对象,以便我们设置http请求中的header信息
              req = urllib.request.Request(url, headers=headers)
              resp = urllib.request.urlopen(req)
              print(resp.read().decode()[:50])
      if __name__ == '__main__':
              t1=threading.Thread(target=get_web,args=('https://www.baidu.com/',))  # target是需要做的事情,利用args传参(元组形式)
              t2=threading.Thread(target=get_web,args=('https://www.sohu.com/',))
              t1.start()  # 启动线程,就会执行target的内容
              t2.start()
              t1.join()  # 等待子线程结束后,才执行主线程
              t2.join()
              print("程序运行结束!")  # 主线程的代码
    • 【注】join方法:让子线程阻塞主线程的执行过程,也就是说使用了join之后,主线程会等待使用了join方法的子线程结束后再往下执行。
  • 运行结果:
2.2、通过继承于Thread类来创建新线程
  • 通过继承Thread类,重写__init__和run方法,__init__中可以添加许多自定义属性,run中是该线程要完成的任务。线程启动后,会自动执行__init__和run方法。
  • import threading
    import urllib.request
    # 新的线程类   
    class MyThread(threading.Thread):  # 继承Thread
            def __init__(self,name,url):
                    super().__init__()  # 调用父类的初始化方法
                    self.name=name  # 线程的名字
                    self.url=url
            # 启动线程后,run会被自动调用
            def run(self):  # 重写父类的run方法,定义在新的线程类里面要完成的任务
                    print(f"我是{self.name}")
                    user_agent = '在从自己的浏览器里找'
                    headers = {
                            "User-Agent": user_agent
                    }
                    # 构造Request对象,以便我们设置http请求中的header信息
                    req = urllib.request.Request(self.url, headers=headers)
                    resp = urllib.request.urlopen(req)
                    print(resp.read().decode()[:50])
    if __name__ == '__main__':
            t1=MyThread("线程1","https://www.baidu.com/")
            t2=MyThread("线程2","https://www.sohu.com/")
            t1.start()  # 启动线程
            t2.start()
            t1.join()  # 等待子线程结束后,才执行主线程
            t2.join()
            print("程序运行结束!")  # 主线程的代码
  •  运行结果:

三、锁

1.1、锁的概念

  • 锁是多线程模块里的一个对象,只有拥有这个对象(锁),才能对共享资源进行操作。
  • 作用:为了避免多个线程使用同一资源时产生错误。

1.2、GIL锁

  • GIL锁:全局解释器锁,在同一个进程中只要有一个线程获取了全局解释器(cpu)的使用权限,那么其他的线程就必须等待该线程的全局解释器(cpu)使用权消失后才能使用全局解释器(cpu),即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用cpu。
  • GIL锁释放条件:
    • 当前的执行线程在执行IO操作时,会主动放弃GIL。
    • 当前执行的线程执行了100条字节码的时候,会自动释放GIL锁。
  • 【注】GIL全局解释器锁是粗粒度的锁,必须配合线程模块中的锁才能对每个原子操作(不可再拆分的操作)进行锁定。

1.3、如何加锁

  • import threading
    num=0
    lock=threading.Lock()  # 创建锁
    def deposit():
            for i in range(1000000):
                    lock.acquire()  # 获取锁
                    global num
                    num+=1
                    lock.release()  # 释放锁
    def withdraw():
            for i in range(1000000):
                    with lock: # 自动获取锁和释放锁
                            global num
                            num-=1
    if __name__ == '__main__':
            t1=threading.Thread(target=deposit)
            t2=threading.Thread(target=withdraw)
            t1.start()
            t2.start()
            t1.join()
            t2.join()
            print(num)
  • 【注】获取锁和释放锁必须成对出现。

四、线程间通讯

  • 线程间的通信方式有很多,最常用的是利用消息队列进行通讯。 

1.1、消息队列

  • 消息队列是在消息的传输过程中保存消息的容器,主要用于不同线程间任意类型数据的共享。
  • 消息队列最经典的用法就是消费者和生成者之间通过消息管道来传递消息,消费者和生成者是不同的线程。生产者往管道中写消息,消费者从管道中读消息,且一次只允许一个线程访问管道。

1.2、常用接口

  • from queue import Queue
    q =Queue(maxsize=0)  # 初始化,maxsize=0表示队列的消息个数不受限制;maxsize>0表示存放限制
    q.get()  # 提取消息,如果队列为空会阻塞程序,等待队列消息
    q.get(timeout=1)  # 阻塞程序,设置超时时间
    q.put()  # 发送消息,将消息放入队列

1.3、演示

  • 使用生产者和消费者的案例进行演示。 
    • from queue import Queue
      import threading
      import time
      def product(q):  # 生产者
              kind = ('猪肉','白菜','豆沙')
              for i in range(3):
                      print(threading.current_thread().name,"生产者开始生产包子")
                      time.sleep(1)
                      q.put(kind[i%3])  # 放入包子
                      print(threading.current_thread().name,"生产者的包子做完了")
      def consumer(q):  # 消费者
              while True:
                      print(threading.current_thread().name,"消费者准备吃包子")
                      time.sleep(1)
                      t=q.get()  # 拿出包子
                      print("消费者吃了一个{}包子".format(t))
      if __name__=='__main__':
              q=Queue(maxsize=1)
              # 启动两个生产者线程
              threading.Thread(target=product,args=(q, )).start()
              threading.Thread(target=product,args=(q, )).start()
              # 启动一个消费者线程
              threading.Thread(target=consumer,args=(q, )).start()
  • 运行结果:

五、创建多个进程

1、以指定函数作为参数创建进程

  • 与通过Thread类构造器来创建新线程的方法类似,只不过使用的模块不同。
    • import multiprocessing  # 引入多进程模块
      import urllib.request
      # 访问网站
      def get_web(url):  # 访问网站
              user_agent = '在自己的浏览器里找'
              headers = {
                      "User-Agent": user_agent
              }
              # 构造Request对象,以便我们设置http请求中的header信息
              req = urllib.request.Request(url, headers=headers)
              resp = urllib.request.urlopen(req)
              print(resp.read().decode()[:50])
      if __name__ == '__main__':
              p1=multiprocessing.Process(target=get_web,args=('https://www.baidu.com/',))
              p2=multiprocessing.Process(target=get_web,args=('https://www.sohu.com/',))
              p1.start()  # 启动进程
              p2.start()
              p1.join()  # 等待子进程结束后,才执行主进程
              p2.join()
              print("进程操作全部执行完毕!")  # 主进程的代码
  • 运行结果:
    •  

2、继承Process类创建进程

  • 与通过继承于Thread类来创建新线程的方法类似。通过继承Process类,重写__init__和run方法,__init__中可以添加许多自定义属性,run中是该进程要完成的任务。进程启动后,会自动执行__init__和run方法。
    • import multiprocessing
      import urllib.request
      import os  # 拿到进程id
      # 新的进程类   
      class MyProcess(multiprocessing.Process):  # 继承Process
              def __init__(self,name,url):
                      super().__init__()  # 调用父类的初始化方法
                      self.name=name  # 进程的名字
                      self.url=url
              # 启动进程后,run会被自动调用
              def run(self):  # 重写父类的run方法,定义在新的进程类里面要完成的任务
                      print(f"我是{self.name}","当前进程ID为:",os.getpid(),"我的父进程ID为:",os.getppid())
                      user_agent = '在自己的浏览器里找'
                      headers = {
                              "User-Agent": user_agent
                      }
                      # 构造Request对象,以便我们设置http请求中的header信息
                      req = urllib.request.Request(self.url, headers=headers)
                      resp = urllib.request.urlopen(req)
                      print(resp.read().decode()[:50])
      if __name__ == '__main__':
              print("当前主进程的ID为:",os.getpid())
              p1=MyProcess("子进程1","https://www.baidu.com/")
              p2=MyProcess("子进程2","https://www.sohu.com/")
              p1.start()  # 启动进程
              p2.start()
              p1.join()  # 等待子进程结束后,才执行主进程
              p2.join()
              print("进程操作全部执行完毕!")  # 主进程的代码
  •  运行结果:

六、进程间通讯

  • 进程间也可以使用消息队列进行通讯。
    • import multiprocessing
      import time
      def product(q):  # 生产者
              kind = ('猪肉','白菜','豆沙')
              for i in range(3):
                      print(multiprocessing.current_process().name,"生产者开始生产包子")
                      time.sleep(1)
                      q.put(kind[i%3])  # 放入包子
                      print(multiprocessing.current_process().name,"生产者的包子做完了")
      def consumer(q):  # 消费者
              while True:
                      print(multiprocessing.current_process().name,"消费者准备吃包子")
                      time.sleep(1)
                      t=q.get()  # 拿出包子
                      print("消费者吃了一个{}包子".format(t))
      if __name__=='__main__':
              q=multiprocessing.Queue(maxsize=1)  # 创建多进程队列对象
              #  启动两个生产者进程
              p1=multiprocessing.Process(target=product,args=(q,))
              p2=multiprocessing.Process(target=product,args=(q,))
              p1.start()
              p2.start()
              #  启动一个消费者进程
              p3=multiprocessing.Process(target=consumer,args=(q,))
              p3.start()
  • 运行结果:

七、总结

1、多线程和多进程的优缺点

  • 多进程的优点:独立运行,互不影响。
  • 多进程缺点:创建进程的代价非常大。
  • 多线程优点:效率比较高,不会耗费大量资源。
  • 多线程缺点:稳定性较差,一个崩溃后会影响整个进程。

2、使用场景

  • 多进程适用场景:适合计算密集型任务。
  • 多线程适用场景:适合 IO 密集型任务,如文件读取以及爬虫等操作。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python入门教学——多进程和多线程 的相关文章

  • 根据 pandas 中的条件交换列值

    我想按条件重新定位列 如果国家 地区是 日本 我需要将姓氏和名字反向重新定位 df pd DataFrame France Kylian Mbappe Japan Hiroyuki Tajima Japan Shiji Kagawa Eng
  • 为什么方法无法访问类变量?

    我试图理解Python中的变量作用域 除了我不明白为什么类变量不能从其方法访问的部分之外 大多数事情对我来说都很清楚 在下面的例子中mydef1 无法访问a 但如果a可以在全局范围 类定义之外 声明 class MyClass1 a 25
  • 如何有条件地组合两个相同形状的 numpy 数组

    这听起来很简单 但我想我把它想得太复杂了 我想创建一个数组 其元素是从两个形状相同的源数组生成的 具体取决于源数组中哪个元素更大 为了显示 import numpy as np array1 np array 2 3 0 array2 np
  • 无法将 datetime.datetime 与 datetime.date 进行比较

    我有以下代码并收到上述错误 由于我是 python 新手 我无法理解这里的语法以及如何修复错误 if not start or date lt start start date 有一个datetime date 从日期时间转换为日期的方法
  • 如何在 openpyxl 中设置或更改表格的默认高度

    我想通过openpyxl更改表格高度 并且我希望首先默认一个更大的高度值 然后我可以设置自动换行以使我的表格更漂亮 但我不知道如何更改默认高度 唯一的到目前为止 我知道更改表格高度的方法是设置 row dimension idx heigh
  • 使用reduce方法的斐波那契数列

    于是 我看到有人用reduce方法来计算斐波那契数列 这是他的想法 1 0 1 1 2 1 3 2 5 3 对应于 1 1 2 3 5 8 13 21 代码如下所示 def fib reduce n initial 1 0 dummy ra
  • 在 Windows 上将 Word2vec 与 Tensorflow 结合使用

    In 本教程文件 https github com tensorflow models blob master tutorials embedding word2vec py L45通过 Tensorflow 找到以下行 第 45 行 来加
  • 熊猫记忆

    我有冗长的计算 我重复了很多次 因此 我想使用记忆 诸如jug http packages python org Jug and joblib http packages python org joblib memory html 与Pan
  • 如何像在浏览器中一样检索准确的 HTML

    我正在使用 Python 脚本来呈现网页并检索其 HTML 它适用于大多数页面 但对于其中一些页面 检索到的 HTML 不完整 我不太明白为什么 这是我用来废弃此页面的脚本 由于某种原因 每个产品的链接不在 HTML 中 Link http
  • 如何将同步函数包装在异步协程中?

    我在用着aiohttp https github com aio libs aiohttp构建一个 API 服务器 将 TCP 请求发送到单独的服务器 发送 TCP 请求的模块是同步的 对于我来说是一个黑匣子 所以我的问题是这些请求阻塞了整
  • 在 GAE/Python 中放置一次性代码和每次代码的最佳位置在哪里?

    我是 Google App Engine 和 Python 的新手 我无法理解有关在 Google App Engine 上运行的 Python 应用程序的一些基本问题 如果我想要执行代码 对于每个传入的请求 我应该将其放在哪里 我们正在捕
  • Pandas,按最大返回值进行分组 AssertionError:

    熊猫有问题 我想听听你的意见 我有这个数据框 我需要在其中获取最大值 代码就在下面 df stack pd DataFrame 1 0 2016 0 NonResidential Hotel 98101 0 DOWNTOWN 47 6122
  • 如何在 Keras 中使用部分输入进行训练,其余部分用于损失函数

    我是 Keras 新手 正在尝试实现神经网络机器学习模型 输入张量看起来像 X1 X2 和输出 Y 注意 X1 和 X2 是相关的 在模型中 只有 X1 将用于训练 但 X1 和 X2 都将传递给损失函数 该损失函数是 X1 X2 y pr
  • 如何在python中递归复制目录并覆盖全部?

    我正在尝试复制 home myUser dir1 及其所有内容 及其内容等 home myuser dir2 在Python中 此外 我希望副本覆盖中的所有内容dir2 It looks like distutils dir util co
  • python 中的基本矩阵转置

    我尝试了 python 中矩阵转置的最基本方法 但是 我没有得到所需的结果 接下来是代码 A 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 print A def TS A B A for i in range len A
  • django如何将字符串转换为模块?

    我试图了解 django 的另一个神奇之处 它可以将字符串转换为模块 In settings py INSTALLED APPS声明如下 INSTALLED APPS django contrib auth django contrib c
  • DRF:以编程方式从 TextChoices 字段获取默认选择

    我们的网站是 Vue 前端 DRF 后端 在一个serializer validate 方法 我需要以编程方式确定哪个选项TextChoices类已被指定为模型字段的默认值 TextChoices 类 缩写示例 class PaymentM
  • 在 python 查询参数中使用 %20 而不是 + 作为空格

    我使用 python requests 编写了以下 python 脚本 http requests readthedocs org en latest http requests readthedocs org en latest impo
  • 字母尺度和随机文本上的马尔可夫链

    我想使用 txt 文件中的一本书中的字母频率生成随机文本 以便每个新字符 string lowercase 取决于前一个 如何使用马尔可夫链来做到这一点 或者使用每个字母都有条件频率的 27 个数组更简单 我想使用来自的字母频率生成随机文本
  • 如何抑制 Pandas Future 警告?

    当我运行该程序时 Pandas 每次都会给出如下所示的 未来警告 D Python lib site packages pandas core frame py 3581 FutureWarning rename with inplace

随机推荐

  • IOS数据管理

    在 iOS 中 没有直接与 Android 中的 SharePreference 相对应的概念 而是使用不同的机制来处理应用程序的持久化数据 在 iOS 中 你可以使用以下几种方法来保存和读取应用程序的数据 UserDefaults 用户默
  • Halcon (64位)无法卸载或者卸载不彻底,没法再次安装?

    以管理员身份 切换到cmd 1 删除安装目录 rmdir S HALCONROOT 2 查询安装的Halcon版本 reg query HKLM SOFTWARE Wow6432Node MVTec HALCON Windows x64 3
  • 面试常用算法归纳

    面试常用算法归纳 算法时间复杂度 二叉查找树的时间复杂度 递归和分治 递归思维 汉诺塔问题 排序算法 最长子串 子序列 一维dp 有断层 最长递增子序列 最大子数组和 无重复字符的最长子串 买卖股票的最佳时机 二维dp 组合 子集 和排列
  • YOLOv5改进算法之添加CA注意力机制模块

    目录 1 CA注意力机制 2 YOLOv5添加注意力机制 送书活动 1 CA注意力机制 CA Coordinate Attention 注意力机制是一种用于加强深度学习模型对输入数据的空间结构理解的注意力机制 CA 注意力机制的核心思想是引
  • Atmel Studio 7.0 快速上手指南(基于ASF)

    Atmel Studio 7 0 快速上手指南 基于ASF 程序员大本营 pianshen com
  • 【Kubernetes部署篇】K8s图形化管理工具Dasboard部署及使用

    文章目录 一 Dashboard简介 二 Dashboard部署安装 三 配置Dashboard登入用户 1 通过Token令牌登入 2 通过kubeconfig文件登入 四 Dashboard创建容器 五 扩展 一 Dashboard简介
  • switch...case...和if...else...区别

    switch 和 if 都是用来处理分支语句的 那么使用的时候 考虑到代码效率问题 就必须先来了解他们有什么区别 先来看看这两个语句的使用格式 if else if 表达式1 语句1 else if 表达式2 语句2 else if 表达式
  • Altium Designer (AD) 元器件出现绿色叉叉报错的解决办法

    出现报错的原因 元器件的安全间距小于设定的安全间距 但通常情况下 这个问题并不严重 可以理解为是一个警告 不去处理也可以 解决办法 点击菜单栏的工具 T 再点击复位错误标志 M 即可解决报错
  • 一个爬虫代码价值 7000 万

    一个爬虫代码价值 7000 亿 这样的代码你听说过吗 这是一个爬取比特币密钥的代码 比特币相信大家都有听说过 尤其最近比特币价格还突破了 5 万美元大关 现在1 枚比特币就价值 35 万人民币 难怪有句说 币圈一天 人间一年 最近朋友圈关于
  • 登录,注册HTML页面,详细过程

    1 页面说明 登录和注册切换按钮 当点击登录按钮时 显示登录表单 当点击注册按钮时 显示注册表单 每个表单都有对应的 JavaScript 校验函数 校验用户名 邮箱和密码是否为空 如果为空 会弹出警告框 2 效果图展示 3 代码部分 3
  • 手把手教你快速上手人体姿态估计(MMPose)

    最近在研究如何快速实现图像中人体姿态的估计 也就是常见的pose estimation任务 花了些时间 实际对比了AlphaPose BlazePose和MMPose BlazePose主要为移动端设计 AlphaPose安装配置比较麻烦
  • 服务器显卡驱动重装系统,windows7旗舰版系统重装显卡驱动的方法

    在windows7旗舰版电脑中 我们都是需要安装显卡驱动 但是如果显卡驱动安装不合适的话 就会容易导致电脑出现问题 所以如果有碰到安装到不合适的显卡驱动的话我们可以通过重装显卡驱动来解决 那么该怎么操作呢 为此小编这就给大家讲解一下wind
  • 图片 url blob base64 互转

    待补充 url to blob export const urlToBlob async url string gt return new Promise resolve gt fetch url then res gt res blob
  • Nginx

    HTTP和反向代理web服务器 Nginx是一个高性能的HTTP和反向代理web服务器 同时也提供了IMAP POP3 SMTP服务 Nginx是一款轻量级的Web服务器反向代理服务器及电子邮件 IMAP POP3 代理服务器 nginx反
  • 结合 服务器+后端+前端,完成 vue项目 后台管理系统

    目录 以上是项目的服务器php 后端 前端 已经可以正常运行 一 登录 登录页进度条 戳这里Vue项目电商后台管理系统 nprogress 进度条 活在风浪里的博客 CSDN博客 二 侧导航 三 列表页源码 四 角色分配 五 权限页面开发
  • 多线程实现Runable接口和Callable接口的区别

    先看源码callable接口 返回泛型v 可以抛出异常 Runable接口是抽象方法run 没有返回值 不能抛出异常 有异常在run方法内部处理 总结 区别1 两者最大的区别 实现Callable接口的任务线程能返回执行结果 而实现Runn
  • 交换机电口、光口、网络速率的基本概念总结

    电口和光口 千兆网 万兆网 POE 包转发率 背板带宽 交换容量 光纤跳线 电口和光口 电口 电口也即RJ45口 插双绞线的端口 网线 一般速率为10M或100M 即为百兆工业交换机 部分支持1000M 即为千兆交换机 光口 工业以太网交换
  • python sklearn 梯度下降法_Python与机器学习:梯度下降

    梯度下降 Gradient Descent 梯度下降法不是一个机器学习算法 是一种基于搜索的最优化算法 目的是最小化一个损失函数 同样 梯度上升法用于最大化一个效用函数 求解损失函数的最小值有两种方法 1 正规方程求解 上一章已经讲使用线性
  • java多线程和高并发系列三 & Synchronized锁详解

    目录 设计同步器的意义 如何解决线程并发安全问题 同步器的本质就是加锁 synchronized原理详解 synchronized底层原理 Monitor监视器锁 什么是monitor 对象的内存布局 对象头 对象头分析工具 锁的膨胀升级过
  • Python入门教学——多进程和多线程

    目录 一 线程和进程 1 线程和进程的基本概念 2 线程和进程的关系 3 串行 并行和并发 二 创建多个线程 1 线程相关的模块 2 创建线程 2 1 通过Thread类构造器来创建新线程 2 2 通过继承于Thread类来创建新线程 三