python 进程、线程、协程基本使用

    • 1、进程、线程以及协程
      • 【1】进程概念
      • 【2】线程的概念
        • 线程的生命周期
        • 进程与线程的区别
      • 【3】协程(Coroutines)
    • 2、多线程实现
      • 【1】threading模块
      • 【2】互斥锁
      • 【3】线程池
      • 【4】线程应用
    • 3、多进程实现
    • 4、协程实现
      • 【1】yield与协程
      • 【2】asyncio模块
      • 【3】3.8版本+
      • 【4】aiohttp

1. 并发与并行
2. IO密集型任务和计算密集型任务
3. 同步与异步
4. IO模型(IO多路复用)
5. 内核态多线程,用户态多线程

所谓并发编程是指在一台处理器上“同时”处理多个任务。并发是在同一实体上的多个事件**。强调多个事件在同一时间间隔发生。**

1、进程、线程以及协程

【1】进程概念

我们都知道计算机的核心是CPU,它承担了所有的计算任务;而操作系统是计算机的管理者,它负责任务的调度、资源的分配和管理,统领整个计算机硬件;应用程序则是具有某种功能的程序,程序是运行于操作系统之上的。

进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。

多道技术:空间复用+时间复用,于是有了进程!

进程是一种抽象的概念,从来没有统一的标准定义。进程一般由程序、数据集合和进程控制块三部分组成。

例子:我和我的女朋友们的故事我就是CPU,我跟三个女朋友玩就是三个任务1. 我教第一个女朋友做菜,菜谱就是程序,食材就是数据,我做饭的过程就是一个进程(切换,状态保存)2. 我给第二个女朋友治疗脚伤,医疗手册就是程序,医药箱就是数据,治疗脚伤的过程就是第二个进程。。。

进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。在三态模型中,进程状态分为三个基本状态,即运行态,就绪态,阻塞态。在五态模型中,进程分为新建态、终止态,运行态,就绪态,阻塞态。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【2】线程的概念

在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。任务调度采用的是时间片轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明了线程。

线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。

一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成。而进程由内存空间(代码、数据、进程空间、打开的文件)和一个或多个线程组成。

线程的生命周期

当线程的数量小于处理器的数量时,线程的并发是真正的并发,不同的线程运行在不同的处理器上。但当线程的数量大于处理器的数量时,线程的并发会受到一些阻碍,此时并不是真正的并发,因为此时至少有一个处理器会运行多个线程。

在单个处理器运行多个线程时,并发是一种模拟出来的状态。操作系统采用时间片轮转的方式轮流执行每一个线程。现在,几乎所有的现代操作系统采用的都是时间片轮转的抢占式调度方式,如我们熟悉的Unix、Linux、Windows及macOS等流行的操作系统。

我们知道线程是程序执行的最小单位,也是任务执行的最小单位。在早期只有进程的操作系统中,进程有五种状态,创建、就绪、运行、阻塞(等待)、退出。早期的进程相当于现在的只有单个线程的进程,那么现在的多线程也有五种状态,现在的多线程的生命周期与早期进程的生命周期类似。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

线程的生命周期# 创建:一个新的线程被创建,等待该线程被调用执行;
# 就绪:时间片已用完,此线程被强制暂停,等待下一个属于它的时间片到来;
# 运行:此线程正在执行,正在占用时间片;
# 阻塞:也叫等待状态,等待某一事件(如IO或另一个线程)执行完;
# 退出:一个线程完成任务或者其他终止条件发生,该线程终止进入退出状态,退出状态释放该线程所分配的资源。
进程与线程的区别

前面讲了进程与线程,但可能你还觉得迷糊,感觉他们很类似。的确,进程与线程有着千丝万缕的关系,下面就让我们一起来理一理:

  1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
  2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
  3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其它进程不可见;
  4. 调度和切换:线程上下文切换比进程上下文切换要快得多。

【3】协程(Coroutines)

协程(Co-routine),也可称为微线程,或非抢占式的多任务子例程,一种用户态的上下文切换技术(通过一个线程实现代码块间的相互切换执行)。这种由程序员自己写程序来管理的轻量级线程叫做『用户空间线程』,具有对内核来说不可见的特性。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。

协程解决的是线程的切换和内存开销的问题

* 用户空间 首先是在用户空间, 避免内核态和用户态的切换导致的成本。
* 由语言或者框架层调度
* 更小的栈空间允许创建大量实例(百万级别)

2、多线程实现

【1】threading模块

Python提供两个模块进行多线程的操作,分别是threadthreading,前者是比较低级的模块,用于更底层的操作,一般应用级别的开发不常用。

import timedef foo():print("foo start...")time.sleep(5)print("foo end...")def bar():print("bar start...")time.sleep(3)print("bar end...")# 串行版本
# start = time.time()
# foo()
# bar()
# end = time.time()
# print("cost timer:", end - start)# 多线程并发版本import threadingstart = time.time()
t1 = threading.Thread(target=foo, args=())
t1.start()
t2 = threading.Thread(target=bar, args=())
t2.start()# 等待所有子线程结束
# t1.join()  # 等待子线程t1
# t2.join()  # 等待子线程t2
end = time.time()
print(end - start)

【2】互斥锁

import time
import threadingLock = threading.Lock()def addNum():global num  # 在每个线程中都获取这个全局变量# 上锁Lock.acquire()t = num - 1time.sleep(0.0001)num = tLock.release()# 放锁num = 100  # 设定一个共享变量thread_list = []for i in range(100):t = threading.Thread(target=addNum)t.start()thread_list.append(t)for t in thread_list:  # 等待所有线程执行完毕t.join()print('Result: ', num)

【3】线程池

系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。在这种情形下,使用线程池可以很好地提升性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。

线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。

此外,使用线程池可以有效地控制系统中并发线程的数量。当系统中包含有大量的并发线程时,会导致系统性能急剧下降,甚至导致解释器崩溃,而线程池的最大线程数参数可以控制系统中并发线程的数量不超过此数。

import time
from concurrent.futures import ThreadPoolExecutordef task(i):print(f'任务{i}开始!')time.sleep(i)print(f'任务{i}结束!')return istart = time.time()
pool = ThreadPoolExecutor(3)future01 = pool.submit(task, 1)
# print("future01是否结束", future01.done())
# 当程序使用 Future 的 result() 方法来获取结果时,该方法会阻塞当前线程,如果没有指定 timeout 参数,当前线程将一直处于阻塞状态,直到 Future 代表的任务返回。
# print("future01的结果", future01.result())  # 同步等待
future02 = pool.submit(task, 2)
future03 = pool.submit(task, 3)
pool.shutdown()  # 阻塞等待
print(f"程序耗时{time.time() - start}秒钟")print("future01的结果", future01.result())
print("future02的结果", future02.result())
print("future03的结果", future03.result())

使用线程池来执行线程任务的步骤如下:

  1. 调用 ThreadPoolExecutor 类的构造器创建一个线程池。
  2. 定义一个普通函数作为线程任务。
  3. 调用 ThreadPoolExecutor 对象的 submit() 方法来提交线程任务。
  4. 当不想提交任何任务时,调用 ThreadPoolExecutor 对象的 shutdown() 方法来关闭线程池。

【4】线程应用

import requests
from lxml import etree
import os
import asyncio
import time
import threadingdef get_img_urls():res = requests.get("https://www.pkdoutu.com/photo/list/", headers={"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"})selector = etree.HTML(res.text)img_urls = selector.xpath('//li[@class="list-group-item"]/div/div/a/img[@data-backup]/@data-backup')print(img_urls)return img_urlsdef save_img(url):res = requests.get(url)name = os.path.basename(url)with open("imgs/" + name, "wb") as f:f.write(res.content)print(f"{name}下载完成!")def main():img_urls = get_img_urls()# 串行[save_img(url) for url in img_urls]# 协程并发t_list = []for url in img_urls:t = threading.Thread(target=save_img, args=(url,))t.start()t_list.append(t)for t in t_list:t.join()if __name__ == '__main__':start = time.time()main()end = time.time()print(end - start)

针对IO密集型任务,Python多线程可以发挥出不错的并发作用

3、多进程实现

由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。

multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。

python的进程调用:

import multiprocessing
import timedef foo():print("foo start...")time.sleep(5)print("foo end...")def bar():print("bar start...")time.sleep(3)print("bar end...")if __name__ == '__main__':start = time.time()t1 = multiprocessing.Process(target=foo, args=())t1.start()t2 = multiprocessing.Process(target=bar, args=())t2.start()# 等待所有子线程结束t1.join()  # 等待子线程t1t2.join()  # 等待子线程t2end = time.time()print(end - start)

4、协程实现

协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

【1】yield与协程


def foo():print("OK1")yield 100  # 切换: 保存/恢复的功能print("OK2")yield 1000def bar():print("OK3")yield 200print("OK4")yield 2000gen = foo()
ret = next(gen)    # gen.__next__()
print(ret)gen2 = bar()
ret2 = next(gen2)  # gen.__next__()
print(ret2)ret = next(gen)    # gen.__next__()
print(ret)ret2 = next(gen2)  # gen.__next__()
print(ret2)

【2】asyncio模块

asyncio即Asynchronous I/O是python一个用来处理并发(concurrent)事件的包,是很多python异步架构的基础,多用于处理高并发网络请求方面的问题。

为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。

asyncio 被用作多个提供高性能 Python 异步框架的基础,包括网络和网站服务,数据库连接库,分布式任务队列等等。

asyncio 往往是构建 IO 密集型和高层级 结构化 网络代码的最佳选择。

import asyncioasync def task(i):print(f"task {i} start")await asyncio.sleep(1)print(f"task {i} end")# 创建事件循环对象
loop = asyncio.get_event_loop()
# 直接将协程对象加入时间循环中
tasks = [task(1), task(2)]
# asyncio.wait:将协程任务进行收集,功能类似后面的asyncio.gather
# run_until_complete阻塞调用,直到协程全部运行结束才返回
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

task: 任务,对协程对象的进一步封装,包含任务的各个状态;asyncio.Task是Future的一个子类,用于实现协作式多任务的库,且Task对象不能用户手动实例化,通过下面2个函数loop.create_task() 或 asyncio.ensure_future()创建。

import asyncio, timeasync def work(i, n):  # 使用async关键字定义异步函数print('任务{}等待: {}秒'.format(i, n))await asyncio.sleep(n)  # 休眠一段时间print('任务{}在{}秒后返回结束运行'.format(i, n))return i + nstart_time = time.time()  # 开始时间tasks = [asyncio.ensure_future(work(1, 1)),asyncio.ensure_future(work(2, 2)),asyncio.ensure_future(work(3, 3))]loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
loop.close()print('运行时间: ', time.time() - start_time)
for task in tasks:print('任务执行结果: ', task.result())

【3】3.8版本+

async.run() 运行协程
async.create_task()创建task
async.gather()获取返回值


import asyncio, timeasync def work(i, n):  # 使用async关键字定义异步函数print('任务{}等待: {}秒'.format(i, n))await asyncio.sleep(n)  # 休眠一段时间print('任务{}在{}秒后返回结束运行'.format(i, n))return i + ntasks = []
async def main():global taskstasks = [asyncio.create_task(work(1, 1)),asyncio.create_task(work(2, 2)),asyncio.create_task(work(3, 3))]await asyncio.wait(tasks) # 阻塞start_time = time.time()  # 开始时间
asyncio.run(main())
print('运行时间: ', time.time() - start_time)
for task in tasks:print('任务执行结果: ', task.result())

asyncio.create_task() 函数在 Python 3.7 中被加入。

asyncio.gather方法

# 用gather()收集返回值import asyncio, timeasync def work(i, n):  # 使用async关键字定义异步函数print('任务{}等待: {}秒'.format(i, n))await asyncio.sleep(n)  # 休眠一段时间print('任务{}在{}秒后返回结束运行'.format(i, n))return i + nasync def main():tasks = [asyncio.create_task(work(1, 1)),asyncio.create_task(work(2, 2)),asyncio.create_task(work(3, 3))]# 将task作为参数传入gather,等异步任务都结束后返回结果列表response = await asyncio.gather(tasks[0], tasks[1], tasks[2])print("异步任务结果:", response)start_time = time.time()  # 开始时间asyncio.run(main())print('运行时间: ', time.time() - start_time)

【4】aiohttp

我们之前学习过爬虫最重要的模块requests,但它是阻塞式的发起请求,每次请求发起后需阻塞等待其返回响应,不能做其他的事情。本文要介绍的aiohttp可以理解成是和requests对应Python异步网络请求库,它是基于 asyncio 的异步模块,可用于实现异步爬虫,有点就是更快于 requests 的同步爬虫。安装方式,pip install aiohttp。

aiohttp是一个为Python提供异步HTTP 客户端/服务端编程,基于asyncio的异步库。asyncio可以实现单线程并发IO操作,其实现了TCP、UDP、SSL等协议,aiohttp就是基于asyncio实现的http框架。

import aiohttp
import asyncioasync def main():async with aiohttp.ClientSession() as session:async with session.get("http://httpbin.org/headers") as response:print(await response.text())asyncio.run(main())

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EasyDarwin 、ffmpeg 音视频推流拉流;OBS视频推理软件、obs-rtspserver服务器;python读取rtsp流

参考:https://blog.csdn.net/N71FS1/article/details/130019563 一、EasyDarwin ffmpeg ffmpeg 推送音视频流到rtsp流服务器 EasyDarwin 作为rtsp流服务器 (下载:https://www.easydarwin.org/p/easydarwin.html)OBS 直播音视频录…

是德科技keysight N9000B 信号分析仪

181/2461/8938产品概述: 工程的内涵就是将各种创意有机地联系起来,并解决遇到的问题。 CXA 信号分析仪具有出色的实际性能,它是一款出类拔萃、经济高效的基本信号表征工具。 它的功能十分强大,为一般用途和教育行业的用户执行测试…

【Linux】体验一款开源的Linux服务器运维管理工具

今天为大家介绍一款开源的 Linux 服务器运维管理工具 - 1panel。 一、安装 根据官方那个提供的在线文档,这款工具的安装需要执行在线安装, # Redhat / CentOScurl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start…

【学习心得】神经网络知识中的符号解释

这里我对我学到的神经网络知识中,常见的符号做一下记录和总结,方便自己在后面学习中复习。下图二分类识别图像识别猫为例。为了保存一张图片,需要三个矩阵,它们分别对应图片中的红、绿、蓝三种颜色通道,如果图片大小为…

Docker 部署 FRP 内网穿透 实现端口映射

Frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议,且支持 P2P 通信。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。 官网地址:https://github.com/fatedier/frp 准备工作…

Linux重点思考(上)--权限/解压/定时任务/性能

Linux重点思考(上)--权限/解压/定时任务 权限修改格式chmod使用 打包解压tar -zcvf ab.tar(打包)tar -xvf ab.tar -C/usr (解压) 系统防火墙service iptables statussystemctl status iptables区别 定时任务定时任务-c…

Map集合

1、Map集合实例化 步骤&#xff1a; 导包&#xff0c;java.util.Map包通过HashMap创建对象语法格式&#xff1a; Map<引用数据类型,引用数据类型> map new HashMap<>(); HashMap类的特点&#xff1a; HashMap是一个散列表&#xff0c;它存储的…

hadoop 高可用(HA)、HDFS HA、Yarn HA

目录 hadoop 高可用(HA) HDFS高可用 HDFS高可用架构 QJM 主备切换&#xff1a; Yarn高可用 hadoop 高可用(HA) HDFS高可用 HDFS高可用架构 QJM 主备切换&#xff1a; Yarn高可用

00、SpringBatch 4.x.x版本:简单入门

00、SpringBatch批处理 一、介绍1、什么是批处理&#xff1f;2、官网3、优势4、组织架构5、程序运行架构图 二、入门案例-H2版(内存)1、新建项目2、引入依赖3、新建HelloJob.java 三、入门案例-MySQL版1、引入依赖2、修改 application.yml3、验证 四、案例解析1、EnableBatchPr…

华为昇腾asend

昇腾Ascend C编程语言 Ascend C原生支持C/C编程规范&#xff0c;通过多层接口抽象、并行编程范式、孪生调试等技术&#xff0c;极大提高了算子的开发效率&#xff0c;帮助AI 参考文章 手把手教你在昇腾平台上搭建PyTorch训练环境 - 哔哩哔哩 (bilibili.com)https://www.bilibi…

Ubuntu20.04安装MatlabR2018a

一、安装包 安装包下载链接 提取码&#xff1a;kve2 网上相关教程很多&#xff0c;此处仅作为安装软件记录&#xff0c;方便后续软件重装&#xff0c;大家按需取用。 二、安装 1. 相关文件一览 下载并解压文件后&#xff0c;如下图所示&#xff1a; 2. 挂载镜像并安装 2…

【Go】五、流程控制

文章目录 1、if2、switch3、for4、for range5、break6、continue7、goto8、return 1、if 条件表达式左右的()是建议省略的if后面一定要有空格&#xff0c;和条件表达式分隔开来{ }一定不能省略if后面可以并列的加入变量的定义 if count : 20;count < 30 {fmt.Println(&quo…

node.js的常用命令

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

数据结构:单调栈和单调队列

文章目录 一、单调栈1.1、栈的思想1.2、单调栈1.2.1、单调栈的基本应用&#xff1a;找出数组中每个元素右侧第一个更大的元素1.2.2、单调栈的基本应用&#xff1a;找出数组中每个元素左侧第一个更大的元素1.2.3、单调栈拓展1.2.4、单调栈LeetCode题单 二、单调队列2.1、队列的思…

Linux系统中安装一些常用的插件备用

Linux系统中安装一些常用的插件备用 1.安装wget yum -y install wget 2.安装vim yum -y install vim-enhanced 3.更换yum源为国内的阿里云源&#xff08;选择&#xff09; 1、备份CentOS-Base.repo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.…

盘点2024年最佳WordPress外贸主题推荐

WordPress是一个非常受欢迎的建站平台&#xff0c;它具有易用性&#xff0c;并提供了许多功能强大的主题和插件。如果你计划建立一个外贸独立站商城&#xff0c;选择一个适合的WordPress外贸主题至关重要。以下是一些外贸主题应具备的特点&#xff1a; 1. 欧美风格&#xff1a…

计算机网络-UDP协议——沐雨先生

实验内容 1.编写并运行UDP server 2.编写并运行UDP client 3.使用Wireshark抓取并分析UDP用户数据报 基本要求 1.了解UDP上server socket与client socket之间的通信过程 2.学会使用Java语言编写UDP socket服务程序 3.学会使用Java语言编写UDP socket客户程序 4.了解UDP…

【C语言基础】:自定义类型(一)--> 结构体

文章目录 一、内置类型与自定义类型1.1 内置类型&#xff08;基本数据类型&#xff09;1.2 自定义类型 二、结构体2.1 结构体的声明2.2 结构体变量的创建和初始化2.3 结构体的特殊声明2.4 结构体的自引用 三、结构体内存对齐3.1 对齐规则3.2 为什么存在内存对齐3.3 修改默认对齐…

再见 mysql_upgrade

在数据库管理的世界里&#xff0c;随着技术的不断进步和业务的不断发展&#xff0c;数据库的版本升级成为了一个不可避免的过程。 MySQL 作为业界领先的开源关系型数据库管理系统&#xff0c;其版本迭代与功能优化同样不容忽视。 而在这个过程中&#xff0c;升级工具就显得尤为…

如何在 Mac 上打开、编辑、复制、移动或删除存储在 Windows NTFS 格式 USB 驱动器上的文件 Tuxera NTFS for Mac使用教程

当您获得一台新 Mac 时&#xff0c;它只能读取 Windows NTFS 格式的 USB 驱动器。要将文件添加、保存或写入您的 Mac&#xff0c;您需要一个附加的 NTFS 驱动程序。Tuxera 他可以帮忙实现这一功能&#xff01; Tuxera可以轻松转换驱动器&#xff1a;无论使用Windows PC还是Mac&…