多线程
2021-05-18 12:30
标签:现在 open 解决 是的 ·· html 问题 那是 inf 多线程初识 threading 包 :threading.Thread创建多线程的两种方式 多线程的组件:threading包中的模块: GIL—全局解释器锁 一、多线程初识: 进程,线程,协程概览: 二.使用threading.Thread 创建多线程: (1)和进程一样,多线程也有两种创建方法: 四.GIL—全局解释器锁: 多线程 标签:现在 open 解决 是的 ·· html 问题 那是 inf 原文地址:https://www.cnblogs.com/gkx0731/p/9744671.html#线程特点
#1.轻型实体,占用非常小的资源
#2.独立调度和分派的基本单位:即cup实际上调度的是线程
#3.共享进程资源
#4.可并发执行
线程与进程的区别可以归纳为以下4点:
1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
3)调度和切换:线程上下文切换比进程上下文切换要快得多。
4)在多线程操作系统中,进程不是一个可执行的实体。
#进程是最小的内存分配单位
#线程是操作系统调度最小单位
#线程被CPU执行了
#进程内至少含有一个线程,称为主线程
#进程中可以开启多个线程
#开启一个线程所需要的时间要远远小于开启一个进程
#多个线程内部有自己的数据栈,数据不共享。
#全局变量在多个线程之间是共享的
#注意:子进程中不能用input,子线程中是可以使用的
import time
import os
from threading import Thread
def func(a,b):
n = a + b
print(‘in the n‘,n)
# print(‘func pid: ‘, os.getpid())
global g
g = 0
print(g)
g = 100
t_lst = []
for i in range(10):
# print(‘pid‘,os.getpid())
t = Thread(target=func,args=(i,5,))
t_lst.append(t)
t.start()
[t.join() for t in t_lst]
print(‘g ‘,g)
#进程
#当前写的代码,导入的模块,文件所在的位置,内置函数
#这些和操作系统,python解释器,占用内存大的都放进程里,线程不放
#线程
#主线程:i呀,t呀这些参数
#子线程:
# 栈:图在有道笔记0928。这里用文字描述。
# 栈用矩形表示,先进后出。比如在子线程中 a=0,b=5,操作方式为add,输出为n,这些一次入栈
# 由于n后入栈,n出来后,得到add,再得到 a,b从而就获得了n的值
#进程的缺点:
# 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
# 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
# 60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端,
# 一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程;二是由于对称多处理机(SMP)出现,
# 可以满足多个运行单位,而多个进程并行开销过大。
# 因此在80年代,出现了···能独立运行的基本单位——线程(Threads)。
# ······注意:进程是资源分配的最小单位,线程是CPU调度的最小单位.
#所以说进程间都有各自的内存空间,只能通信来获取数值
# 每一个进程中至少有一个线程
#可以把进程看做一个车间,每个进程肯定有一个主线程,类比车间必定有一个工人(主线程)
#把线程看作车间的工人
#CPU就是生产零件的机器,工人搬材料给cpu制作。此时如果要多生产零件,我们平时都是在一直增加车间(即增加进程),从而多了工人来运作零件制造
#但是增加线程,就好比只在一个车间里增加工人,就每必要每次都要新开车间,时空开销这么大了
#在操作系统中的 分时调度系统里,我们所说的切换进程,其实就是切换进程的主线程
#内存中的进程:
#从头到尾 栈,堆,数据,文本
#进程中有:
#代码,数据,文件 线程(线程有 寄存器,栈,线程本身)
# 线程有 寄存器,栈,线程本身
# 线程与进程的区别可以归纳为以下4点:
# 1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
# 2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
# 3)调度和切换:线程上下文切换比进程上下文切换要快得多。
#有时也称做进程切换或任务切换,是指CPU 从一个进程或线程切换到另一个进程或线程。
# 4)在多线程操作系统中,进程不是一个可执行的实体。
#真正执行的是线程
#http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
#线程特点
#1.轻型实体,占用非常小的资源
#2.独立调度和分派的基本单位:即cup实际上调度的是线程
#3.共享进程资源
#4.可并发执行
#协程:
#本质上是一个线程
#能够在多个任务之间切换来节省一些IO时间
#协程中任务之间的切换也消耗时间,但是开销远远小于进程线程之间的切换
#协程的意义:
#在遇到IO操作的时候,切换到另外一个任务
#规避之前任务的IO时间,来提高cpu的利用率
#在实际工作中会采用:进程+线程+协程,来提高代码的并发效果
#进程是cpu核数+1,线程是cpu核数*5,每个线程中协程最多可以起500个
#比如:
#发送了一个网页请求后,在网络延时,等待网页响应的时间(等待IO),就可以用协程去切换任务利用等待的时间,继续发送多个网页请求,从而提高效率
#进程5,线程20,协程500 = 总共可以有50000个协程:一台4c的机器最多可以接收的并发数
#数据库,负载均衡,让很多个请求,平均分摊给各个服务器
#nginx组件 大型互联网公司会用到,就是用来帮你分发任务的,并发最大承载量就是50000,用的就是协程机制
#一般情况下就是根据这个规则,上下浮动
#主要用threading
# Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。
# thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。
# Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
#
import time
import os
from threading import Thread
def func(n):
time.sleep(1)
print(‘in the n‘,n)
print(‘func pid: ‘,os.getpid())
for i in range(10):
print(‘pid‘,os.getpid())
t = Thread(target=func,args=(1,))
t.start()
#因为不开启进程,所以不用 if __name__ == ‘__main__‘
#其余的启动线程,start线程,就和期用进程一模一样了。各个线程之间也有三态,
# 执行的时候因为是并发的,所以不一定按顺序
#第二种方式,也是启动进程一样。1.继承Thread,2.有run方法,里面是线程代码,3.参数用__init__传入,父类参数记得super().__init__()
# class Mythread(Thread):
# def __init__(self,n):
# super().__init__()
# self.n = n
# def run(self):
# print(‘in the run‘,self.n+1)
#
# m = Mythread(1)
# m.start()
# https://docs.python.org/3/library/threading.html?highlight=threading# 线程官方说明
# import requests
# url = r‘https://docs.python.org/3/library/threading.html?highlight=threading#‘
# res = requests.get(url)
# print(res.status_code)
# print(res.content)
#GIL 全局解释器锁
#我们现在的CPU基本上是4核以上,完全可以同一时刻同时执行几个线程
#但是同一时刻执行,就有可能发生,两个不同线程,同时向进程获取全局变量,操作完后写入这个全局变量后,会产生混乱
#所以在 Cpython解释器里加了一把锁,这个锁就是 全局解释器锁,简称GIL
#当A线程找进程拿了数据后,此时其他线程就拿不到对应的数据,只有当A线程处理完数据,返回给进程后,其他线程才能使用
#因此GIL 使得 同一时刻只能有一个线程访问CPU
#GIL锁住的是线程,
#那是因为python语言的问题吗,不是的 是 cpython解释器的特性。jpython就 java语言写的解释器,就没有GIL。不过我们现在用的都是cpython
#编译型解释器,会在编译过程中大大的回避这个问题
#但是目前还没有完善的方案,去解决GIL的问题
#高CPU:所以在高CPU使用率的的程序中,比如计算类,确实python不占优势
#但是我们一般写的程序很少用到这么连续使用CPU的情况。如果非要用到高计算类的,那就可以使用多进程
#高IO:我们主要写的是高IO的程序,#而且在高计算类的时候,一般也不使用多线程
#高并发:爬取200个网页,大部分时间都在等待网络延迟,不是高计算
# QQ聊天
#处理日志文件,大部分时间在等待读文件,写文件(IO)
#处理web请求, 还有比如登陆验证,读写数据库
#GIL运行步骤,再次强调锁是锁住线程
# Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,
# 同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
# 对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
# 在多线程环境中,Python 虚拟机按以下方式执行:
# a、设置 GIL;
# b、切换到一个线程去运行;
# c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
# d、把线程设置为睡眠状态;
# e、解锁 GIL;
# d、再次重复以上所有步骤。
#协程
#由于cpython解释器中的GIL原因,导致python中多线程被弱化了,而且切换多个线程之间也要时间开销
#所以就出现了协程,协程的切换效率更快,把1个线程的作用发挥到了极致,提高1个cpu的利用率。减少线程的时间开销
#java里也有协程,但是没有这么被重视