Python进程与线程开发

目录

multiprocessing模块

线程的开发

threading模块 

 setDaemon

死锁 

 线程间的通信


multiprocessing模块

运行python的时候,我们都是在创建并运行一个进程,(linux中一个进程可以fork一个子进程,并让这个子进程exec另外一个程序)。在python中,我们通过标准库中的subprocess包来fork一个子进程,并且运行一个外部的程序。subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所欲我们可以根据需要来从中选取一个使用。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。

multiprocessing 包同时提供本地和远程并发,使用子进程代替线程,有效避免 Global Interpreter Lock 带来的影响。

from multiprocessing import Process
import os
from time import sleep, time
def test1(name):
  
  print("当前进程的ID", os.getpid())
  print("父进程的ID", os.getppid())
  print("当前进程的名字:", name)
  
  sleep(3)

if __name__ == '__main__':
  start = time()
  # 创建多个子进程,并且把这些子进程放入列表中
  process_list = []
  print("主进程的ID", os.getpid())
  for i in range(10):
    
    p = Process(target=test1, args=('process-%s' % i,))
    
    p.start()
    process_list.append(p)

类包装:自定义一个Process进程类,该类中的run函数由一个子进程调用执行

from multiprocessing import Process
import os
from time import sleep, time
# 自定义一个进程类 继承Process类
class MyProcess(Process):
  def __init__(self, name):
    Process.__init__(self)
    self.name = name
  def run(self):
    
    print("当前进程的ID", os.getpid())
    print("父进程的ID", os.getppid())
    print("当前进程的名字:", self.name)
    sleep(3)

if __name__ == '__main__':
  print("主进程ID", os.getpid())
  
  start = time()
  process_list = []
  for i in range(10):
    
    p = MyProcess("process-%s" % i)
    
    p.start()
    process_list.append(p)
  for p in process_list:
    
    p.join()
    
    end = time() - start
    print(end)

线程的开发

Python 的标准库提供了两个模块:thread 和 threading,thread 是低级模块,threading 是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading 这个高级模块。

多线程概念

多线程使得系统可以在单独的进程中执行并发任务。虽然进程也可以在独立的内存空间中并发执行,但是其系统开销会比较大。生成一个新进程必须为其分配独立的地址空间,并维护其代码段、堆栈段和数据段等,这种开销是巨大的。另外,进程间的通信实现也不方便。在程序功能日益复杂的时候,需要有更好的系统模型来满足要求,线程由此产生了。 线程是“轻量级”的,一个进程中的线程使用同样的地址空间,且共享许多资源。启动线程的时间远远小于启动进程的时间和空间,而且,线程间的切换也要比进程间的切换快得多。由于使用同样的地址空间,所以线程之间的数据通信比较方便,一个进程下的线程之间可以直接使用彼此的数据。当然,这种方便性也会带来一些问
题,特别是同步问题。 多线程对于那些I/O受限的程序特别适用。其实使用多线程的一个重要目的,就是最大化地利用CPU的资源。当某一线程在等待I/O的时候,另外一个线程可以占用CPU资源。如最简单的GUI程序,一般需要有一个任务支持前台界面的交互,还要有一个任务支持后台的处理。 

多线程运行的作用:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
  • 程序的运行速度可能加快。
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等。在这种情况下我们可以释放一些珍贵的资源如内存占用。 

线程可以分为内核线程和用户线程,内核线程由操作系统内核创建和撤销;用户线程不需要内核支持而在用户程序中实现的线程。

创建线程 

Python3 通过两个标准库 _thread 和 threading 提供对线程的支持。

threading模块 

import time
def say():
  print("这是单线程!")
  time.sleep(1)
if __name__ == "__main__":
  start = time.time()
  for i in range(5):
    say()
  end = time.time()
  print(f'使用时间:{end - start}')
import threading
import time
def say():
  print("这是多线程")
  time.sleep(1)
if __name__ == "__main__":
  '''入口'''
  start = time.time()
  for i in range(5):
    # 通过threading下的Thread方法创建线程
    t = threading.Thread(target=say)
    t.start()  # 启动线程
  end = time.time()
  print(f'使用时间:{end - start}')

 可以明显看出使用了多线程并发的操作,花费时间要短很多。

程序是通过threading模块下的Thread的类,去实例化一个此对象,并调用方法,实现生成多线程,target参数表示线程需要执行的方法,通过对象的start的方法,开启线程。

import threading
from time import sleep,ctime
def sing():
  for i in range(3):
    print("唱歌...%d"%i)
    sleep(1)
def dance():
  for i in range(3):
    print("跳舞...%d"%i)
    sleep(1)
if __name__ == '__main__':
  print('---开始---:%s'%ctime())
  t1 = threading.Thread(target=sing)
  t2 = threading.Thread(target=dance)
  t1.start()
  t2.start()
  sleep(5) 
  print('---结束---:%s'%ctime())

 join方法

该方法将等待,一直到它调用的线程终止,它的名字表示调用的线程会一直等待,直到指定的线程加入它。join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止。

from threading import Thread
from time import sleep, time
def run(name):
  
  print("Threading:{} start".format(name))
  sleep(3)
  print("Threading:{} end".format(name))
if __name__ == '__main__':
  
  # 开始时间
  start = time()
  # 创建线程列表
  t_list = []
  # 循环创建线程
  for i in range(10):
    t = Thread(target=run, args=('t{}'.format(i),))
    t.start()
    t_list.append(t)
  # 等待线程结束
  for t in t_list:
    t.join()
  # 计算使用时间
  end = time() - start
  print(end)

 setDaemon

将线程声明为守护线程,在start() 方法调用之前设置,这个方法和join是相反的。

有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,
这时就可以用setDaemon方法。

from threading import Thread
import time
def foo():
  print(123)
  time.sleep(1)
  print("end123")
def bar():
  print(456)
  time.sleep(3)
  print("end456")
t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.daemon = True
t1.start()
t2.start()
print("~~~")

同步锁与GIL的关系 

GIL本质是一把互斥锁,但GIL锁住的是解释器级别的数据,同步锁,锁的是解释器以外的共享资源,例如:硬盘上的文件 控制台,对于这种不属于解释器的数据资源就应该自己加锁处理 。

GIL 的作用是:对于一个解释器,只能有一个thread在执行bytecode。所以每时每刻只有一条bytecode在被执行一个thread。GIL保证了bytecode 这层面上是thread safe的。

死锁 

在多线程程序中,死锁问题很大一部分是由于线程同时获取多个锁造成的。
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。 

产生死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个线程使用。
  • 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:线程已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。 
from threading import Thread,Lock
from time import sleep
class Task1(Thread):
  def run(self):
    while True:
      if lock1.acquire():
        print("------Task 1 -----")
        sleep(0.5)
        lock2.release()
class Task2(Thread):
  def run(self):
    while True:
      if lock2.acquire():
        print("------Task 2 -----")
        sleep(0.5)
        lock3.release()

class Task3(Thread):
  def run(self):
    while True:
      if lock3.acquire():
        print("------Task 3 -----")
        sleep(0.5)
        lock1.release()

lock1 = Lock()
#创建另外一把锁
lock2 = Lock()
lock2.acquire()

lock3 = Lock()
lock3.acquire()
t1 = Task1()
t2 = Task2()
t3 = Task3()
t1.start()
t2.start()
t3.start()

 线程间的通信

在加锁的情况下,程序就变成了串行,也就是单线程,而有时,我们在不用考虑数据安全时,不用加锁,程序就变成了并行,也就是多线程。为了避免业务开启过多的线程时。我们就可以通过信号量(Semaphore)来设置指定个数的线程。 

from threading import Thread, BoundedSemaphore
from time import sleep
def an_jian(num):
  semapshore.acquire()
  print('第{}个人安检完成!'.format(num))
  sleep(2)
  semapshore.release()

if __name__ == '__main__':
  semapshore = BoundedSemaphore(3)
  for i in range(20):
    thread = Thread(target=an_jian, args=(i,))
    thread.start()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/466120.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

算法设计与分析(贪心法)

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需…

Vulnhub - Symfonos

希望和各位大佬一起学习,如果文章内容有错请多多指正,谢谢! 个人博客链接:CH4SER的个人BLOG – Welcome To Ch4sers Blog Symfonos 靶机下载地址:https://www.vulnhub.com/entry/symfonos-1,322/ 0x01 信息收集 …

[保姆级教程]Windows安装MongoDB教程

文章目录 MongoDB安装包下载1.点击进入mongodb官网2.点击MongoDB Community Edition(社区版),进入下图界面3.选择版本4.下载5.安装6.勾选同意协议,点击“Next"7.选择自定义安装8.点击“Next"9.修改到合适的地址10.点击i…

影响汇率的因素?fpmarkets澳福总结几个

汇率对于刚刚开始外汇交易的新手来说非常重要,这不是没有道理的,了解汇率如何变化以及怎么变化有助于在外汇交易中获得稳定的利润。那么影响汇率的因素有哪些?fpmarkets澳福总结几个。 任何国家货币的汇率都是由市场决定的。主要的市场因素是…

盲盒抽卡机小程序开发:开启惊喜之旅,探索无限可能

随着互联网的快速发展,消费者的购物体验也在不断升级。盲盒文化,以其独特的魅力和惊喜感,正逐渐成为年轻人追求潮流、享受乐趣的新选择。为了满足广大盲盒爱好者的需求,我们精心打造了这款盲盒抽卡机小程序,为用户带来…

代码随想录算法训练营第43天 | 1049.最后一块石头的重量II ,494.目标和,474.一和零

动态规划章节理论基础: https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 1049.最后一块石头的重量II 题目链接:https://leetcode.cn/problems/last-stone-weight-ii/ 思路: …

阿里巴巴国际站商品采集商品信息抓取API免费测试入口(英文商品信息跨境电商商品信息自动化抓取)

alibaba.item_get 获取商品详情信息 alibaba.item_search 关键字搜索商品列表 进入API测试页,获取key和密钥 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)secretString是调用密钥api_nameString是API接口名称…

Docker学习之使用harbor搭建私有仓库(超详解析)

实验目的: 使用centos7,基于harbor构建私有仓库 实验步骤: 下载相关安装包和依赖: [rootlocalhost ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 wget //安装docker所需要的相关依赖 [rootlocalhost ~]#…

中国休闲装行业深度调研分析

环洋咨询Global Info Research的休闲装市场调研报告提供休闲装市场的基本概况,包括定义,分类,应用和产业链结构,同时还讨论发展政策和计划以及制造流程和成本结构,分析休闲装市场的发展现状与未来市场趋势,…

java入门-变量与常量

java 基本语法-变量与常量 变量 变量的本质 程序中我们会经常看到类似 int x 3**;** 的表达式,x就是我们常说的变量,从计算机角度我们来看看变量x的本质是什么? 在程序开发中定义一个变量x, 计算机会在内存中开辟内存空间,计算…

【C语言基础】:字符函数和字符串函数

文章目录 一、字符函数1. 字符分类函数2. 字符转化函数 二、字符串函数1. strlen函数的使用和模拟实现strlen函数的使用strlen函数的模拟实现 2. strcpy函数的使用和模拟实现strcpy函数的使用strcpy函数的模拟实现 3. strcat函数的使用和模拟实现strcat函数的使用strcat函数的模…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Grid)

网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 仅支持GridItem…

MasterAlign视觉对位软件提示系统校准时间错误解决方案

MasterAlign视觉对位软件提示系统校准时间错误解决方案 一、问题现象 当运行软件时弹出“系统校准时间错误”的提示,如下图: 出现“系统校准时间错误”提示,说明当前系统时间比上一次软件运行时的系统时间提前了,需要修改当前系…

Windows系统搭建web网站并结合内网穿透实现公网访问本地站点

文章目录 使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点,测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中,查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2.2 映…

苹果MacOS电脑使用内网穿透轻松远程桌面本地Windows系统电脑

文章目录 1. 测试本地局域网内远程控制1.1 Windows打开远程桌面1.2 局域网远程控制windows 2. 测试Mac公网远程控制windows2.1 在windows电脑上安装cpolar2.2 Mac公网远程windows 3. 配置公网固定TCP地址 日常工作生活中,有时候会涉及到不同设备不同操作系统之间需要…

JetBrains全家桶激活,分享PyCharm 2024 激活的方案

大家好,欢迎来到金榜探云手! PyCharm 公司简介 JetBrains 是一家专注于开发工具的软件公司,总部位于捷克。他们以提供强大的集成开发环境(IDE)而闻名,如 IntelliJ IDEA、PyCharm、和 WebStorm等。这些工具…

RP2040 VSCode C/C++开发环境快速部署

RP2040 VSCode C/C开发环境快速部署 📌安装参考《树莓派(Raspberry Pi) Pico VSCode C/C开发环境配置(无需Visual Studio)》📍Windows环境下 MSYS2一键式部署pico程序包,下载地址:https://github.com/raspberrypi/pico-setup-wind…

简单使用NSIS打包软件

NSIS是一个开源的打包工具. 官网: Download - NSIS (sourceforge.io) 使用这个编译 ​ 但是不建议使用这玩意写脚本,字体太难看了.我用vscode写的脚本,用这个编译的. ​ 写好脚本用这个软件打开, 然后选择这个编译,如果语法有错误 会编译不过,会提醒你哪一行不行,如果编译…

探讨NLP对行业大量数据信息抽取的技术实现

在本文中,为了实现高效的信息抽取,我们采用了一个自主研发的多模态AI的大模型NLP平台。 这个平台的使用过程分为以下几个步骤: 数据收集:我们收集了与项目相关的100条数据样本,这些样本涵盖了各种商品描述&#xff0c…

一口气看完明朝276年历史

明朝是中国历史上最后一个由汉人建立的大一统封建王朝,建立于公元1368年,亡于公元1644年,国祚276年,传12世16帝。 太祖建国 太祖(1368~1398) 公元1368年,朱元璋在南京应天府建元称帝&#xff…