### 线程
进程: 资源分配最小单位
线程: cpu执行程序的最小单位
一个进程必须有一个线程,一个线程可以包含多个线程
##一份进程资源中可以包含多个线程
from threading import Thread ==>引入线程(属于一个类)
from multiprocessing import Process ==>引入进程(属于一个类)
import os
def func(num):
print('当前线程{},所归属的进程id号{}'.format(os.getpid(),num))
if __name__=="__main__":
for i in range(10):
# 异步创建10个子线程
t = Thread(target=func,args=(i,))
t.start()
主线程执行任务
print(os.getpid())
## 并发多线程和多进程 , 谁的速度更快? 多线程!
import time
def func(num):
print('当前线程{},所归属的进程id号{}'.format(os.getpid(),num))
if __name__=='__main__':
多线程测试;
#记录开始时间
starttime=time.time()
lst=[]
for i in range(1000):
t=Thread(target=func,args=(i,))
t.start()
lst.append(t)
# 等到所有子线程执行完毕
for i in lst:
i.join()
# 记录结束时间
endtime=time.time()
print("多线程执行时间:",endtime-starttime)
多进程测试:
#记录开始时间
starttime=time.time()
lst=[]
for i in range(1000):
p=Process(target=func,args=(i,))
p.start()
lst.append(p)
# 等到所有子线程执行完毕
for i in lst:
i.join()
# 记录结束时间
endtime=time.time()
print("多进程执行时间:",endtime-starttime)
##多线程 共享同一份进程资源
num =1000
def func():
global num
num -=1
if __name__=="__main__":
for i in range(1000):
t=Thread(target=func)
t.start()
print(num)
##用类定义线程
from threading import Thread
import time
import os
class MyThread(Thread): ===>必须继承父类
def __init__(self,name): ==>如果传参数,必须调用父类的构造方法
# 手动调用父类的构造方法
super().__init__() ==>super()只调用父类
self.name = name
def run(self): ===>run方法不可以乱写还有(handle)
time.sleep(1)
print("当前进程正在执行runing ... " , self.name)
if __name__ == "__main__":
t = MyThread("机器今天会再次爆炸么?")
t.start()
print("主线程执行结束 ... ")
##线程相关的函数
线程.is_alive() 检测线程是否仍然存在
线程.setName() 设置线程名字
线程.getName() 获取线程名字
1.currentThread().ident 查看线程id号
2.enumerate() 返回目前正在运行的线程列表
3.activeCount() 返回目前正在运行的线程数量
例如:
def func():
time.sleep(1)
if __name__ == "__main__":
t = Thread(target=func)
t.start()
# print(t , type(t)) ===>对象
print(t.is_alive()) # False ==>判断子线程是否还活着
print(t.getName()) ==>获取名字
t.setName("xboyww") ===>修改子线程名字
print(t.getName()) ===>将修改的名字再去获取
1.currentThread().ident 查看线程id号
from threading import currentThread ==>需要引入(currentThread这个一个类)
例如:
def func():
print("子线程id",currentThread().ident , os.getpid())
if __name__ == "__main__":
Thread(target=func).start()
print("主线程id",currentThread().ident , os.getpid())
2.enumerate() 返回目前正在运行的线程列表(死了的线程不展示)
from threading import enumerate
from threading import activeCount # (了解)
例如:
def func():
print("子线程id",currentThread().ident , os.getpid())
time.sleep(0.5)
if __name__ == "__main__":
for i in range(10):
Thread(target=func).start()
lst = enumerate()
# 子线程10 + 主线程1个 = 11
# print(lst ,len(lst))
# 3.activeCount() 返回目前正在运行的线程数量
print( activeCount() ) ===>打印对象会触发__str__方法
##守护线程 : 等待所有线程全部执行完毕之后,在自己终止,守护的是所有线程
from threading import Thread
import time
def func1():
while True:
time.sleep(0.5)
print("我是func1")
def func2():
print("我是func2 start ... ")
time.sleep(3)
print("我是func2 end ... ")
t1 = Thread(target=func1)
t2 = Thread(target=func2)
在start调用之前,设置守护线程
t1.setDaemon(True)
t1.start()
t2.start()
print("主线程执行结束 ... ")
##Lock 保证线程数据安全
from threading import Lock,Thread
import time
例如:
n = 0 ===>多个线程可以共用一个进程里面的数据
def func1(lock):
global n
for i in range(1000000):
# 方法一
lock.acquire()
n -= 1
lock.release()
def func2(lock):
global n
# 方法二 # with 自动完成上锁+解锁
with lock: ==>加在外面最好
for i in range(1000000):
n += 1
if __name__ == "__main__":
lst = []
lock = Lock()
time1 = time.time()
for i in range(10):
t1 = Thread(target=func1,args=(lock,))
t2 = Thread(target=func2,args=(lock,))
t1.start()
t2.start()
lst.append(t1)
lst.append(t2)
# 等待所有的子线程执行结束之后, 在打印数据
for i in lst:
i.join()
time2 = time.time()
print("主线程执行结束..." , n ,time2 - time1)
##信号量 Semaphore (线程)
from threading import Semaphore,Thread
import time
def func(i,sm):
with sm:
print(i)
time.sleep(3)
if __name__ == "__main__":
sm = Semaphore(5)
for i in range(20):
Thread(target=func,args=(i,sm)).start()
总结:
再创建线程的时候是异步创建
在执行任务的时候,因为Semaphore加了锁,所以线程之间变成了同步;
##事件 Event
from threading import Event,Thread
import random,time
e = Event()
# wait 动态加阻塞
# clear 将阻塞事件的值改成False
# set 将阻塞事件的值改成True
# is_set 获取阻塞事件的值
(1) 基本语法
e = Event()
print(e.is_set())
e.set()
e.clear()
e.wait(3)
print("程序在运行 ... ")
模拟连接远程数据库(阿里)
"""连接三次数据库,如果都不成功,直接抛出异常"""
def check(e): ==>负责执行检测任务
# 模拟网络延迟
time.sleep(random.randrange(1,6))
# 开始检测链接合法性
print("开始检测链接合法性")
e.set() ==>如果检测都合法,将阻塞态变成放行态
def connect(e): ===>负责执行连接任务
sign = False ==.>设置一个状态
for i in range(1,4): ==>设置连接的次数(如果三次都不合法,执行if分支)
# 设置最大等待时间 1
e.wait(1)
if e.is_set():
print("数据库链接成功 ... ")
sign = True
break
else:
print("尝试链接数据%s次失败" % (i) )
if sign == False: ===>如果都不成功,抛出异常
# 主动抛出异常
raise TimeoutError
e = Event()
线程1负责执行检测任务
Thread(target=check,args=(e,)).start()
线程2负责执行连接任务
Thread(target=connect,args=(e,)).start()
##线程队列
from queue import Queue
put 存
get 取
put_nowait 存,超出了队列长度,报错
get_nowait 取,没数据取不出来,报错
linux windows 线程中put_nowait,get_nowait都支持
Queue
"""先进先出,后进后出"""
q = Queue()
q.put(1)
print(q.get())
取不出来,阻塞
print(q.get())
print(q.get_nowait())==>报错
q2 = Queue(3)
q2.put(11)
q2.put(22)
q2.put(33)
放不进去了,阻塞
q2.put(44)
q2.put_nowait(44)
LifoQueue 先进后出,后进先出(按照栈的特点设计)
from queue import LifoQueue
lq = LifoQueue(3)
lq.put(11)
lq.put(22)
lq.put(33)
print(lq.put_nowait(444))
print(lq.get())
print(lq.get())
print(lq.get())
PriorityQueue 按照优先级顺序排序 (默认从小到大排序)
from queue import PriorityQueue
如果都是数字,默认从小到大排序
pq = PriorityQueue()
pq.put(13)
pq.put(3)
pq.put(20)
print(pq.get())
print(pq.get())
print(pq.get())
如果都是字符串
"""如果是字符串,按照ascii编码排序"""
pq1 = PriorityQueue()
pq1.put("chinese")
pq1.put("america")
pq1.put("latinos")
pq1.put("blackman")
print(pq1.get())
print(pq1.get())
print(pq1.get())
print(pq1.get())
要么全是数字,要么全是字符串,不能混合 error
pq2 = PriorityQueue()
pq2.put(13)
pq2.put("aaa")
pq2.put("拟稿")
pq3 = PriorityQueue()
默认按照元组中的第一个元素排序
pq3.put( (20,"wangwen") )
pq3.put( (18,"wangzhen") )
pq3.put( (30,"weiyilin") )
pq3.put( (40,"xiechen") )
print(pq3.get())
print(pq3.get())
print(pq3.get())
print(pq3.get())
总结(进程和线程):
不同点:
1:
进程状态可以共享(Lock,Event,Semaphore,通过soket),资源不共享.共享需要通过队列(Queue,JoinableQueue,Manager) ,进程可以并行和并发
线程状态和资源是共享的(共享同一份进程),可以并发,不可并行.
2:
进程: 资源分配最小单位(划分资源)
线程: cpu执行程序的最小单位
3:
线程的相关函数:
线程(对象).is_alive() 检测线程是否仍然存在
线程(对象).setName(参数) 设置线程名字
线程(对象).getName() 获取线程名字
lst = enumerate() 返回目前正在运行的线程列表 引入from threading import enumerate
相同点:
Lock (锁):
进程锁:
意义:同一时间上一把锁,会在自己内部给各个进程发出消息(状态)
引入锁: from muiltprocessing import Process,Lock
创建一把锁(相当于定义,写在if下面) : lock = Lock()
使用:
上锁 lock.acquire() 简写为:
执行操做.... with lock:
解锁 lock.release() 执行操做....
例子:模拟12306 抢票软件
线程锁:
意义: 保证线程数据安全
引入锁: from threading import Lock,Thread
创建一把锁(相当于定义,写在if下面) : lock = Lock()
使用:
上锁 lock.acquire() 简写为:
执行操做.... with lock:
解锁 lock.release() 执行操做....
Event(事件):
进程(阻塞事件):
引入事件: from multiprocessing import Process,Event
生成事件对象e(相当于定义,写在if下面) :e = Event()
含有的使用方法:
e.wait()动态给程序加阻塞 ,一个程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False] 如果是True 不加阻塞如果是False 加阻塞
e.set()方法 将这个属性的值改成True
e.clear()方法 将这个属性的值改成False
e.is_set()方法 判断当前的属性是否为True (默认上来是False)
例子:模拟红绿灯效果
线程(事件):
引入事件:from threading import Thread ,Event
生成事件对象e(相当于定义,写在if下面) :e = Event()
含有的使用方法:
e.wait()动态给程序加阻塞 ,一个程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值是False] 如果是True 不加阻塞如果是False 加阻塞
e.set()方法 将这个属性的值改成True
e.clear()方法 将这个属性的值改成False
e.is_set()方法 判断当前的属性是否为True (默认上来是False)
例子:模拟连接远程数据库
队列:
进程:
作用:可以让进程之间共享数据
包含:Queue, JoinableQueue, Manager
Queue:
引入:from multiprocessing import Process,Queue
生成对象q(相当于定义,写在if下面) : q = Queue()
可以限定队列的长度: q= Queue(3)===>最多塞3个值,超出了队列的长度,会发生阻塞
特点: 先进先出,后进后出
含有的使用方法:
q.put(参数) 往队列中存值
res=q.get() 从队列中取值 ==>队列里面没数据了,在调用get会发生阻塞
例子: 生产者消费者模型
JoinableQueue:
引入: from multiprocessing import Process,JoinableQueue
生成对象jq(相当于定义,写在if下面) : jq = JoinableQueue()
含有的使用方法:
jq.put(参数) 存储,通过task_done,会让队列计数器+1
res=jq.get() 获取,通过task_done,让队列计数器-1
jq.task_done() 队列计数减1
jq.join() 阻塞,函数,会根据队列中的计数器来判定是阻塞还是放行,如果计数器变量是0,意味着放行,其他情况阻塞,task_done 配合 join 一起使用
例子: 优化生产者消费者模型
Manager :
引入:from multiprocessing import Process,Manager,Lock ==>必须引入Lock(上锁)
生成对象m(相当于定义,写在if下面): m = Manager()
在创建一个共享的字典 or 创建一个共享的列表
data = m.dict( {"count":20000} ) data = m.list([1,2,3])
线程:
包含:Queue,LifoQueue,PriorityQueue
Queue:
引入:from queue import Queue
生成对象q(相当于定义,写在if下面):q = Queue()
可以限定队列的长度: q= Queue(3)===>最多塞3个值,超出了队列的长度,会发生阻塞
特点: 先进先出,后进后出
含有的使用方法:
q.put(参数) 往队列中存值
res=q.get() 从队列中取值 ==>队列里面没值会阻塞
LifoQueue:
引入:from queue import LifoQueue
生成对象lq(相当于定义,写在if下面):lq = LifoQueue()
可以限定队列的长度: lq= Queue(3)===>最多塞3个值,超出了队列的长度,会发生阻塞
特点:先进后出,后进先出
含有的使用方法:
lq.put(参数) 往队列中存值
res=lq.get() 从队列中取值 ==>队列里面没值会阻塞
PriorityQueue:
意义: 按照优先级顺序排序 (默认从小到大排序)
引入:from queue import PriorityQueue
生成对象pq(相当于定义,写在if下面):pq = PriorityQueue()
含有的使用方法:
pq.put(参数) 往队列中存值
res=pq.get() 从队列中取值 ==>队列里面没值会阻塞
1:如果都是数字,默认从小到大排序
pq.put(12)
pq.put(23)
res=pq.get()
2:如果是字符串,按照ascii编码排序,小的先排(要么全是数字塞,要么全是字符串塞,不能混合塞)
pq.put('china')
pq.put('america')
res=pq.get()
3:默认按照元组中的第一个元素排序(如果混合塞,要塞成元组形势)
pq.put( (30,"weiyilin") )
pq.put( (40,"xiechen") )
res=pq.get()
Semaphore(锁):
进程:
意义:本质上就是锁,可以控制上锁的数量
引入:from multiprocessing import Semaphore,Process
生成对象sm(相当于定义,写在if下面):sm= Semaphore()
可以限定锁的数量: sm= Semaphore(3)===>最多塞3个值,超出了会发生阻塞
使用:
上锁 sm.acquire() 简写为:
执行操做.... with sm:
解锁 sm.release() 执行操做....
线程:
意义:本质上就是锁,可以控制上锁的数量
引入:from threading import Semaphore,Thread
生成对象sm(相当于定义,写在if下面):sm= Semaphore()
可以限定锁的数量: sm= Semaphore(3)===>最多塞3个值,超出了会发生阻塞
使用:
上锁 sm.acquire() 简写为:
执行操做.... with sm:
解锁 sm.release() 执行操做....
守护:
守护进程:
作用:守护进程守护的是主进程,如果主进程执行结束了,意味着守护进程的寿命立刻终止.立刻杀死
语法:
进程(对象).daemon = True ==>设置当前进程为守护进程必须写在start()调用进程之前进行设置
守护线程:
作用: 等待所有线程全部执行完毕之后,在自己终止,守护的是所有线程
语法:
线程(对象).setDaemon(True) ==>在start调用之前,设置守护线程 ==>注意D大写
id:
进程id:
引入:import os
当前进程的父进程id(父进程的进程号)
res= os.getppid()
当前进程的子进程id(子进程的进程号)
res= os.getppid()
线程id:
引入:from threading import currentThread
查看线程id号
currentThread().ident
创建:
创建进程:
格式为:
引入:from multiprocessing import Process
if __name__ == "__main__":
p = Process(target=func,args=(n,))
p.start()
创建线程:
格式为:
引入:from threading import Thread
if __name__ == "__main__":
t = Thread(target=func,args=(i,))
t.start()
另外:
1:进程和线程上锁都会从异步变成同步,有先有后,如果必须要同步执行操作(有先有后),将执行操作的内容前后进行上锁(上锁和解锁是一对)
2:一个进程必须有一个线程,一个线程可以包含多个线程
3:使用进程和线程(可以不引)的时候,要引入 if __name __ ==’__main __’:
4;在定义锁后,函数在使用锁时,会把锁返回的对象当做参数传进去
5:并发多线程和多进程 , 多线程的速度更快!
6:线程锁一般加在外面,进程锁一般是修改哪里加在哪里
7:当创建进程或者线程的时候是异步的.当执行某个方法时候,各个进程或者线程之间是同步的
8:在定义事件后,函数在使用事件时,会把事件返回的对象当做参数传进去
9:进程相当于一块地,线程是地里面干活的工人(工人可以是多个)
10:如果wait中有参数.代表是最多阻塞几秒,等待几秒后就开始执行,如果不加参数,会一直阻塞(除非将False改为True)
11:在使用Queue时,为了等待子进程把数据塞到队列中,在获取,要加一个join
12:在定义进程Queue和JoinableQueue后,函数在使用Queue和JoinableQueue时,会把Queue和JoinableQueue返回的对象当做参数传进去
13;在定义Manager后,然后在创建一个共享字典或者列表,函数在使用Manager时,会把创建共享字典或者共享列表返回的数据当做参数传进去
14:在使用Manage时候,必须要使用锁和join.
15:在进程的queue 或者jionablequeue共享数据是一个进程将数据传到另外一个进程中,是属于获取或者是储存,manager共享数据是进程修改共享的数据
16:在定义线程Queue和LifoQueue,PriorityQueue后,函数在使用Queue和Queue和LifoQueue,PriorityQueue时,会把Queue和Queue和LifoQueue,PriorityQueue返回的对象当做参数传进去
17:每一个进程必须有一个主线程,一个主线程可以包含多个子线程
18:并发(一个CPU执行多个)多线程和多进程 , 多线程速度更快(线程不需要开辟空间,进行要开辟空间)
19:线程和进程都是异步并发的程序(一个CPU执行多个),有阻塞会跳过,一个进程里面的线程也会创建多个子线程.
20:创建的多线程或者多进程返回的都是对象
21;多个线程在一个进程里面公用一个进程号(进程号id相同),但每个线程都有自己的id
22:线程的enumerate() 返回目前正在运行的线程列表,字典推导式中的enumerate是枚举,和 zip (拉链) 是一对 ,用来做字典推导式的,返回迭代器
23: 计算数据(多线程或者多进程)的时候在同步情况下才会准确,所以要加锁,进程和线程一样.因为在异步的情况下,可能在第一个程序算的时候还没有把结果放回去,第二个程序就开始算了.可能会导致重复.所以针对于异步修改同一份数据的时候,一定要加锁,(例如抢票,manager修改数据,线程的修改数据)
24:id要重新引入 ,lock等直接在引入进程(线程)后面即可,并且lock等要定义,他们所返回值会作为一个参数
25:semaphore可以控制上锁的数量(最多线程或者进程几个),线程和进程使用都一样(唱吧),lock只控制一个
26:进程和线程都是异步创建和同步执行(需要上锁)
27:python中的线程可以并发,但是不能并行(同一个进程下的多个线程不能分开被多个cpu同时执行),因为GIL锁.进程可以并发和并行
28:全局解释器锁(GIL)同一时间,一个进程下的多个线程只能被一个CPU执行,不能实现线程的并行操作. 解决方法:1:用多进程间接实现线程的并行.2:更换解释器(Pypy)