day38

今日内容概要

  • 进程和线程的比较

  • GIL全局解释器锁(重要理论)

  • 互斥锁

  • 线程队列(线程里使用队列)

  • 进程池和线程池的用法

  • 协程理论

  • 如何使用协程

  • 基于协程的高并发城程序

进程和线程比较

1.进程的开销比线程的开销大很多
2.进程之间的数据是隔离的,但是,线程之间的数据不隔离
3.多个进程之间的线程数据不共享----->还是让进程通信(IPC)------>进程下的线程也通信了----->队列

GIL全局解释器锁

python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然python解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行

对python解释器的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行

背景
1.python代码运行在解释器上,由解释器来执行或解释
2.python解释器的种类:CPython  IPython PyPy  Jython  IronPython
3.当前市场使用的最多的解释器就是CPython解释器
4.GIL全局解释器锁是存在于CPython中
5.结论是同一时刻只有一个线程在执行? 想避免的问题是,出现多个线程抢夺资源的情况
    比如:现在起一个线程,来回收垃圾数据,回收a=1这个变量,另外一个线程也要使用这个变量a,当垃圾回收线程还没没有把变量a回收完毕,另一个线程就来抢夺这个变量a使用。
    怎么避免的这个问题,那就是在Python这门语言设计之处,就直接在解释器上添加了一把锁,这把锁就是为了让统一时刻只有一个线程在执行,言外之意就是哪个线程想执行,就必须先拿到这把锁(GIL), 只有等到这个线程把GIL锁释放掉,别的线程才能拿到,然后具备了执行权限.

“GIL锁就是保证在同一时刻只有一个线程执行,所有的线程必须拿到GIL锁才有执行权限”

记忆问题
1.python有GIL锁的原因,同一个进程下多个线程实际上同一时刻,只有一个线程在执行
2.只有python上开进程用的多,其他语言一般不开多进程,只开多线程就够了
3.cpython解释器开多线程不能利用多核优势,只有开多进程才能利用多核优势,其他语言不存在这个问题
4.8核cpu电脑,充分利用8核,至少起8个线程,8条线程全是计算---->计算机cpu使用率是100%
5.如果不存在GIL锁,一个进程下,开启8个线程,它能够充分利用cpu资源,跑满cpu
6.cpython解释器中好多代码,模块都是基于GIL锁机制写起来的,改不了了--->我们不能有8个核,但我现在只能用1核---->开启多进程---->每个进程下开启的线程,可以被多个cpu调度执行
7.cpython解释器:io密集型使用多线程,计算机密集型使用多进程

   io密集型,遇到io操作会切换cpu,假设开启了8个线程,8个线程都有io操作----> io操作不消耗cpu--->一段时间看上去,其实8个线程都执行了,选多线程好一些

   计算机密集型,消耗cpu,如果开了8个线程,第一个线程会一直占着cpu,而不会调度到其他线程执行,其他7个线程根本没执行,所以我们开8个线程,每个进程有一个线程,8个进程下的线程会被8个cpu执行,从而效率高
计算密集型选多进程好一些,在其他语言中,都是选择多线程,而不是多进程

互斥锁

在多线程的情况下,同时执行一个数据,会发生数据错乱的问题

n = 10
from threading import Lock
import time
def task():global ntemp = ntime.sleep(0.5)n = temp - 1lock.release()from threading import Threadif __name__ == '__main__':tt = []for i in range(10):t = Thread(target=task,)t.start()tt.append(t)for j in tt:j.join()print("主",n)
# 主 9n = 10
from threading import Lock
import time
def task():global ntemp = ntime.sleep(0.5)n = temp - 1"""拿时间换空间,空间换时间 时间复杂度"""
from threading import Threadif __name__ == '__main__':tt = []for i in range(10):t = Thread(target=task, )t.start()tt.append(t)for j in tt:j.join()print("主", n)
# 主,0

面试题:既然有了GIL锁,为什么还要互斥锁?(多线程下)
    比如:起了2个线程,来执行a=a+1,a一开始是0
    1.第一个线程来了,拿到0,开始执行a=a+1,这个时候结果a就是1了
    2.第一个线程得到的结果1还没有赋值回去给a,这个时候,第二个线程来了,拿到a=0,继续执行   a=a+1
    3.加了互斥锁,就能解决多线程下操作同一个数据,发生错乱的问题

线程队列

同一个进程下多个线程数据是共享的,为什么先同一个进程下还会去使用队列呢?
因为队列是 管道+锁 所以用队列还是为了保证数据的安全

先进先出
class queue.Queue(maxsize=0)
import queueq=queue.Queue()
q.put('first')
q.put('second')
q.put('third')print(q.get())
print(q.get())
print(q.get())
'''
结果(先进先出):
first
second
third
'''
进程Queue用于父进程与子进程(或同一父进程中多个子进程)间数据传递
python自己的多个进程间交换数据或者与其他语言(如Java)进程queue就无能为力queue.Queue 的缺点是它的实现涉及到多个锁和条件变量,因此可能会影响性能和内存效率。
后进先出
class queue.LifoQueue(maxsize=0)
import queueq=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third')print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
third
second
first
'''
存储数据时可设置优先级的队列
class queue.PriorityQueue(maxsize=0)
优先级队列
import queueq=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''

进程池和线程池的作用

进程池:提前定义好一个池子,然后,往这个池子里面添加进程,以后,只需要往这个进程池里面丢任务就行了,然后,有这个进程池里面的任意一个进程来执行任务
线程池:提前定义好一个池子,然后,往这个池子里面添加线程,以后,只需要往这个线程池里面丢任务就行了,然后,有这个线程池里面的任意一个线程来执行任务

def task(n, m):return n + mfrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutordef callback(res):print(res.result())if __name__ == '__main__':pool = ProcessPoolExecutor(3)pool.submit(task, m=1, n=2).add_done_callback(callback)pool.shutdown()print(123)

多线程爬取网页

import requestsdef get_page(url):res=requests.get(url)name=url.rsplit('/')[-1]+'.html'return {'name':name,'text':res.content}def call_back(fut):print(fut.result()['name'])with open(fut.result()['name'],'wb') as f:f.write(fut.result()['text'])if __name__ == '__main__':pool=ThreadPoolExecutor(2)urls=['http://www.baidu.com','http://www.cnblogs.com','http://www.taobao.com']for url in urls:pool.submit(get_page,url).add_done_callback(call_back)

协程理论

协程:是单线程下的并发,又称微线程。一句话说明什么是协程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

需要强调的是:
1.python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
2.单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(非io操作的切换与效率无关)
对比操作系统控制线程的切换,用户在单线程内控制协程的切换

优点如下:
1.协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
2.单线程内就可以实现并发效果,最大限度利用cpu

缺点如下:
1.协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
2.协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

总结协程特点:

1.必须在只有一个单线程里实现开发
2.修改共享数据不需要加锁
3.用户程序里自己保存多个控制流的上下文栈
4.附加:一个协程遇到io操作自动切换到其他协程
    

协程实现高并发

服务端:
from gevent import monkey;monkey.patch_all()
import gevent
from socket import socket
# from multiprocessing import Process
from threading import Threaddef talk(conn):while True:try:data = conn.recv(1024)if len(data) == 0: breakprint(data)conn.send(data.upper())except Exception as e:print(e)conn.close()def server(ip, port):server = socket()server.bind((ip, port))server.listen(5)while True:conn, addr = server.accept()# t=Process(target=talk,args=(conn,))# t=Thread(target=talk,args=(conn,))# t.start()gevent.spawn(talk, conn)if __name__ == '__main__':g1 = gevent.spawn(server, '127.0.0.1', 8080)g1.join()客户端:import socket
from threading import current_thread, Threaddef socket_client():cli = socket.socket()cli.connect(('127.0.0.1', 8080))while True:ss = '%s say hello' % current_thread().getName()cli.send(ss.encode('utf-8'))data = cli.recv(1024)print(data)for i in range(5000):t = Thread(target=socket_client)t.start()

猴子补丁

猴子补丁的功能(一切皆对象)

拥有在模块运行时替换的功能,例如:一个函数对象赋值给另外一个函数对象(把函数原本的执行的功能给替换了)

class Monkey():def play(self):print('猴子在玩')class Dog():def play(self):print('狗子在玩')
m=Monkey()
m.play()
m.play=Dog().play
m.play()
monkey patch的应用场景

这里有一个比较实用的例子,很多用到import json,后来发现ujson性能更高,如果觉得把每个文件的import json改成import ujson as json成本较高,或者说想测试一下ujson替换是否符合预期,只需要在入口加上:

import json
import ujsondef monkey_patch_json():json.__name__ = 'ujson'json.dumps = ujson.dumpsjson.loads = ujson.loads
monkey_patch_json()
aa=json.dumps({'name':'lqz','age':19})
print(aa)

Gevent介绍

gevent是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是gevent,它是以C扩展模块形式接入Python的轻量级协程。Greenlet全部运行在主程序操作系统进程内部,但它们被协作式地调度

用法

#用法
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的g2=gevent.spawn(func2)g1.join() #等待g1结束g2.join() #等待g2结束#或者上述两步合作一步:gevent.joinall([g1,g2])g1.value#拿到func1的返回值

示例1

import gevent
def eat(name):print('%s eat 1' %name)gevent.sleep(2)print('%s eat 2' %name)def play(name):print('%s play 1' %name)gevent.sleep(1)print('%s play 2' %name)g1=gevent.spawn(eat,'lqz')
g2=gevent.spawn(play,name='lqz')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('主')

示例2

'''
上例gevent.sleep(2)模拟的是gevent可以识别的io阻塞,而time.sleep(2)或其他的阻塞,gevent是不能直接识别的需要用下面一行代码,打补丁,就可以识别了from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前或者我们干脆记忆成:要用gevent,需要将from gevent import monkey;monkey.patch_all()放到文件的开头
'''
from gevent import monkey;monkey.patch_all()import gevent
import time
def eat():print('eat food 1')time.sleep(2)print('eat food 2')def play():print('play 1')time.sleep(1)print('play 2')g1=gevent.spawn(eat)
g2=gevent.spawn(play_phone)
gevent.joinall([g1,g2])
print('主')# 我们可以用threading.current_thread().getName()来查看每个g1和g2,查看的结果为DummyThread-n,即假线程

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

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

相关文章

LeetCode 799. 香槟塔【数组,模拟,简单线性DP】1855

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…

货物摆放(蓝桥杯)

货物摆放 题目描述 小蓝有一个超大的仓库,可以摆放很多货物。 现在,小蓝有 n 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。 小蓝希望所有的…

jdk 加密 aes jar包解决

JDK1.8.0_151的无限制强度加密策略文件变动 JDK1.8.0_151无需去官网下载 local_policy.jar US_export_policy.jar这个jar包,只需要修改Java\jdk1.8.0_151\jre\lib\security这目录下的java.security文件配置即可。 随着越来越多的第三方工具只支持 JDK8&#xff0c…

开源OA协同办公系统,集成Flowable流程引擎 可拖拽创建个性表单

源码下载:https://download.csdn.net/download/m0_66047725/88403340 源码下载2: 关注我留言 开源OA协同办公系统,集成Flowable流程引擎 可拖拽创建个性表单。基于RuoYi-VUE版本开发。 1、使用RuoYi-Vue的基础上开发。 2、集成flowable&a…

JSON.stringify()和JSON.parse()的使用

JSON.parse()方法:用来解析 JSON 字符串,就是把JSON字符串转成对象。例如: JSON字符串:let Array "[{\"name\": \"zs\"}, {\"name\": \"ls\"}]" …

ubuntu 20.04 passwd 指令不能使用

Linux 更改用户密码报Changing password for user 用户名. passwd: Module is unknown或更改新增用户密码passwd:未知的用户名 报错信息如下: 解决方法: 可以排查 /etc/pam.d/passwd配置文件 注释掉包含pam_passwdqc.so模块的行&#xff0c…

.NET开源且免费的Windows远程桌面管理软件

前言 今天要给大家推荐一款由.NET开源且免费的远程桌面管理软件:1Remote。 1Remote官方项目介绍 1Remote是一款现代的远程会话管理和启动器,它让你能够在任何时候快速开启一个远程会话。目前1Remote已支持 微软远程桌面(RDP)、VNC、SSH、Telnet、SFTP、…

Hadoop3教程(二十四):Yarn的常用命令与参数配置实例

文章目录 (132)YARN常用命令查看任务查看日志查看容器查看节点状态rmadmin更新配置查看队列 (133)生产环境核心配置参数(135)生产环境核心参数配置案例(140/141)Tool接口案例参考文献…

企业IT资产设备折旧残值如何计算

环境: 企业/公司 IT资产 问题描述: 企业IT设备折旧残值如何计算? 解决方案: 1.按三年折旧 净值原值-月折旧额折旧月份 , 月折旧额原值(1-3%)/36 折旧月份ROUND(E2*(1-3%)/36,2) 2.净值E2-F2*G2

彩虹商城知识付费程序

1,下载程序, 2.宝塔新建站点,,自己的域名直接用(别忘记解析了)教程直接用IP测试。。 3.上传你下载的压缩包(这里暂停一下,传好了继续)有点慢等不了了, 4.上传…

风储联合系统并网时对储能系统影响的研究

风储联合系统并网时对储能系统影响的研究 摘要 风能是目前国内外应用较为广泛的一种绿色可再生能源,近几年我国风电产业的发展十分迅速。然后,越来越多的风力发电系统建并网,风力发电产生的电能受外界因素影响较大,具有一定的随…

C语言中的3种注释方法

C语言中的3种注释方法 2021年8月28日星期六席锦 在用C语言编程时,常用的注释方式有如下几种: (1)单行注释 // … (2)多行注释 /* … */ (3)条件编译注释 #if 0…#endif (1)(2)在入门教程中比较常见。 对于(1) 【单行注释 // …】,注释只能显示…

Linux 下使用 Docker 安装 MySQL

1、下载 mysql docker pull mysql:5.7.362、启动 mysql docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysql \ -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD123456 \ -d mysql:5.7.36命令解…

【VR】【Unity】如何调整Quest2的隐藏系统时间日期

【背景】 网络虽然OK,但是Oculus Quest要连上商店还必须调整好系统时间,不过在Quest系统中,时间对用户是不可见的,本篇介绍调整的方法。 【方法】 打开SideQuest,没有的话先去下载一个。打开后先登录,如…

安装宝塔面板(详细教程)

一、简介 宝塔面板是一款简单好用的服务器运维面板。它支持一键LAMP/LNMP/集群/监控/网站/FTP/数据库/JAVA等100多项服务器管理功能。对于新手用云服务器来建站的话,宝塔面板是一个非常好用的工具。 宝塔官网:https://www.bt.cn/new/index.html 二、宝…

HZOJ-322: 程序自动分析

题目描述 ​ 在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。 ​ 考虑一个约束满足问题的简化版本:假设 x1,x2,x3,…�1,�2,�3,… 代表程序中出现的变量,给定n�个形如xixj&#x…

万物归宗系列01-html基本语法

万物归宗系列&#xff0c;即什么都懂一点系列。 HTML是标签语言&#xff0c;一般成双成对。 Hypertext Markup Language&#xff1a;超⽂本标记语⾔。是⽤来制作⽹页的⼀种标记语⾔。 1 基本框架 <!DOCTYPE html> <html lang"en"> <head><meta…

Linux命令(103)之wc

linux命令之wc 1.wc介绍 linux命令wc是用来统计文件的字数、行数和字节数 2.wc用法 wc [参数] [filename] wc参数 参数说明-l统计总行数&#xff0c;备注&#xff1a;常用于查看进程是否启动-L统计最长一行的字符数-c统计字节数-m统计字符数-w统计单词数 3.实例 3.1.统计…

Pytorch训练深度强化学习时CPU内存占用一直在快速增加

最近在用MATD3算法解决多机器人任务&#xff0c;但是在训练过程中&#xff0c;CPU内存一直在增加&#xff08;注意&#xff0c;不是GPU显存&#xff09;。我很头疼&#xff0c;以为是算法代码出了问题&#xff0c;导致了内存泄漏&#xff0c;折腾了1天也没解决。后来用memory_p…

057:mapboxGL中layout,paint等属性的函数表达说明

第057个 点击查看专栏目录 本篇文章是mapbox的layer中layout,paint等属性的函数表达 mapbox中 Function 是什么 函数 Function 可以作为其 layout布局类属性和 paint 绘制类属性的属性值。在使用 Function 作为属性值时,实际上是一个对象。 layers的3种函数类型 Function …