python多线程
定义:
并行:真的多任务
并发:假的多任务
不同进程之间靠队列来实现
程序运行起来,叫进程,进程是资源分配的单位,线程执行代码,一个线程只能执行一个任务,想要执行多个任务,就需要多线程
协程依赖于线程,线程依赖于进程,协程切换需要资源相当少,所以效率就会很高
重构:把现在代码推倒,重新写
进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源很大,效率很低
线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
GIL即全局解释锁的缩写,保证了了同一时刻只有一个线程在一个CPU上执行字节码,
无法将多个线程映射到多个CPU上
协程切换任务资源很小,效率很高
多进程,多线程根据cpu核数不一样可能是并行的,但是协程是一个线程中,所以是并发的
每一个程序都有一个主线程,主线程然后分出来子线程,线程运行没有先后顺序,可以通过时间延迟
迭代器
for temp in obj:
pass
1.判断obj是否是可以迭代的(有__iter__函数是可以迭代的,有__iter__函数跟__next__函数是迭代器)
2.在第一步成立的前提下,调用iter函数,得到obj对象的__iter__方法的返回值,
3.__iter__方法的返回值是一个迭代器
函数名+():这个是调用函数
函数名:这个是告诉函数在哪里
当调用Thread的时候,不会创建线程
当调用Thread创建出来的实例对象的start方法地时候,才会创建线程以及让这个线程开始运行
类:封装,继承,多态
在一个函数中,对全局变量进行修改的时候,到底是否需要使用global进行说明,要看是否对全局
变量的执行指向进行了修改:
如果修改了执行,即让全局变量指向了一个新地方,那么必须使用global,如果,仅仅是修改了指向
的空间数据,此时不用必须使用global.
代码变成了进程:进程是资源分配的单位,线程实现了多任务(调度的单位),线程不能独立执行,必须依存在进程中
进程:把一台电视机的零件都给你
线程:把零件组装
多进程:(写时拷贝)
进程:能够完成多任务,比如,在一台电脑上能够运行
迭代器:迭代是取数据的过程.先判断集合中是否有数据,(使用极少的空间,返回生成的方式)
如果有,就取出一个数据,接着再判断集合中是否有数据, 如果有再接着取出一个数据,这样往复循环直到所有数据都取出来了.
为什么要有迭代器:单列集合分为有序 list 集合和无序set集合
List接口有索引,我们可以通过for循环+get方法来获取数据,但是Set接口这边没有索引,不能通过for循环+get方式获取数据.
所以Collection接口就搞了一种通用的方式方便所有的集合来获取数据,就是迭代器(Iterator)
多线程:
import threading # 线程模块
import time
def sing():
print("唱歌")
def danche():
print("跳舞")
def main():
for i in range(5):
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=danche)
t1.start()
t2.start()
if __name__ =="__main__":
main()
多线程传递参数问题
import threading # 线程模块
import time
def sing():
print("唱歌")
def danche():
print("跳舞")
def main():
for i in range(5):
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=danche)
t1.start()
t2.start()
if __name__ =="__main__":
main()
查看线程主线程
import time
def test1():
for i in range(5):
print("test1")
def test2():
for i in range(5):
print("test2")
def main():
t1=threading.Thread(target=test1)
t2 = threading.Thread(target=test2)#不仅可以写函数名,还可以
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print(threading.enumerate())
if __name__ == "__main__":
main()
多线程掉用类
#使用情况满足条件
#一个线程里面做的事情比较复杂,我分成多个函数来做,一般创建一个类
import threading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg="I'm"+self.name+'@'+str(i)# name属性中保存的是当前线程的名字
print(msg)
self.login()#为了达到多线程的目的,只用在run()函数里面调用
self.register()
def login(self):
print("这是登陆。。。")
def register(self):
print("这个是注册。。。")
def main():
t=MyThread()
t.start() # 只会运行类里面run()方法,没有运行不了,其他不运行
if __name__ == "__main__":
main()
全局变量资源竞争问题
import time
# 定义一个全局变量
g_num=0
def test1():
global g_num
for i in range(1000000):
g_num +=1
"""1.获得g_num的值
2.对g_num加1
3.把所得结果赋值给g_num
"""
def test2():
global g_num
for i in range(1000000):
g_num+=1
def main():
#target指定将来 这个线程去哪个函数执行代码
#args指定将来调用 函数的时候 传递什么数据过去(元组)
t1=threading.Thread(target=test1)
t2=threading.Thread(target=test2)
t1.start()
t2.start()
time.sleep(5)
print("主线程%d"%g_num)
if __name__ == "__main__":
main()
多线程解决资源竞争问题:
import threading
import time
# 定义一个全局变量
g_num=0
def test1():
global g_num
#上锁,如果之前没有上锁,那么此时上锁成功
#如果上锁之前已经被上锁了,那么此时会堵塞这里,直到这个索恩被解开为止
mutex.acquire()
for i in range(1000000):
g_num +=1
#解锁
"""1.获得g_num的值
2.对g_num加1
3.把所得结果赋值给g_num
"""
mutex.release()
def test2():
global g_num
mutex.acquire()
for i in range(1000000):
g_num+=1
mutex.release()
#创建一个互斥锁,默认是没有上锁的
mutex=threading.Lock()
def main():
#target指定将来 这个线程去哪个函数执行代码
#args指定将来调用 函数的时候 传递什么数据过去(元组)
t1=threading.Thread(target=test1)
t2=threading.Thread(target=test2)
t1.start()
t2.start()
time.sleep(10)
print("主线程%d"%g_num)
if __name__ == "__main__":
main()
通过队列完成进程之间通信
import multiprocessing
def download_from_web(q):
#模拟从网上下载的数据
data=[11,22,33,44]
#向队列中写入数据
for temp in data:
q.put(temp)
print(temp)
def analysis_data(q):
waitting_analysis_data=list()
#从对列中获取数据
while True:
data=q.get()
waitting_analysis_data.append(data)
if q.empty():
break
print(waitting_analysis_data)
def main():
#1.创建一个队列
q=multiprocessing.Queue()
#2.创建多个进程,将队列的引用当作实参进行传递里面
p1=multiprocessing.Process(target=download_from_web,args=(q,))
p2 = multiprocessing.Process(target=analysis_data,args=(q,))
p1.start()
p2.start()
pass
if __name__ == "__main__":
main()
创建进程池:
import multiprocessing
import random
import time, os
def worker(msg):
t_start=time.time()# 开始执行时间
print("%s开始执行,进程号为%d"%(msg,os.getpid()))
#random.random()随机生成0-1之间的浮点数
time.sleep(random.random()*2)
t_stop=time.time()# 执行结束时间
print(msg,"执行完毕,耗时0.2f"%(t_stop-t_start))
def main():
# po=multiprocessing.pool(3)# 定义一个进程池,最大数为3
po = multiprocessing.Pool(3)
for i in range(0, 10):
# pool().apply_async(要调用的目标,(传递给目标的参数元组,))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker, (i,))
print("---start---")
po.close() # 关闭进程池,关闭后po不在接受新的请求
po.join() # 等待po中的进程执行完,必须放到close语句之后,如果不使用进程池,主进程会等各个分支执行完再结束
print("---end---")
if __name__ == "__main__":
main()
yield完成多任务:
import time
def task_1():
while True:
print("---1---")
time.sleep(1)
yield
def task_2():
while True:
print("---2---")
time.sleep(1)
yield
def main():
t1=task_1()
t2=task_2()
while True:
next(t1)
next(t2)
pass
if __name__ == "__main__":
main()
yield生成器
ef create_num(all_num):
a, b = 0,1
current_num = 0
while current_num < all_num:
# print(a)
yield a #如果一个函数中有yield语句,那么这个就不再是函数,而是一个生成器的模板
a, b = b,a+b
current_num += 1
#如果再调用create_num的时候,发现这个函数中有yield,那么此时,不是调用函数,而是创建一个生成器对象
obj = create_num(10)
obj1=create_num(2)# 又创建了一个生成器对象(生成器对象之间是对立的)
res=next(obj)
print(res)
send 启动生成器
def create_num(all_num):
a, b = 0,1
current_num = 0
while current_num < all_num:
# print(a)
ret= yield a #如果一个函数中有yield语句,那么这个就不再是函数,而是一个生成器的模板
print(ret)
a, b = b,a+b
current_num += 1
obj = create_num(10)
ret=next(obj)
print(ret)
ret= obj.send("haha")# (启动生成器)
# send里面的数据会 传递第七行ret结果,然后ret保存这个结果
# send的结果是下一次调用yield时,yield后面的值
print(ret)
# ret=next(obj)
# print(ret)
# ret= obj.send("haha")
# for num in obj:
# print(num)
迭代器:
# 迭代器
from collections.abc import Iterable
# isinstance(a,b)验证第一个参数是不是第二个参数的类型
# print(isinstance([11,22],Iterable))
class Classmate(object):
def __init__(self):
self.names=list()
def add(self,name):
self.names.append(name)
def __iter__(self):
# 如果想要一个对象称为一个可以迭代的对象,既可以使用for,那么必须使用__iter__方法
return classmate1(self)
class classmate1(object):
def __init__(self,obj):
self.obj=obj
self.current_num=0
def __iter__(self):
pass
def __next__(self):
if self.current_num<len(self.obj.names):
ret = self.obj.names[self.current_num]
self.current_num+=1
return ret
else:
raise StopIteration# 返回self,需要把下面的__next__弄到类里面,此类里面
classmate=Classmate()
classmate.add("老王")
classmate.add("老张")
classmate.add("老李")
for name in classmate:
print(name)
通过异常判断生成器结束:
# 生成器是一类特殊的迭代器
def create_num(all_num):
a, b = 0,1
current_num = 0
while current_num < all_num:
# print(a)
yield a #如果一个函数中有yield语句,那么这个就不再是函数,而是一个生成器的模板
a, b = b,a+b
current_num += 1
return "ok"
#如果再调用create_num的时候,发现这个函数中有yield,那么此时,不是调用函数,而是创建一个生成器对象
obj = create_num(10)
obj1=create_num(2)# 又创建了一个生成器对象(生成器对象之间是对立的)
res=next(obj)
print(res)
# for num in obj:
# print(num)
while True:
try:
ret=next(obj1)
print(ret)
except Exception as ret:
print(ret.value)
break
通过异常完成进程间通信
import multiprocessing
def download_from_web(q):
#模拟从网上下载的数据
data=[11,22,33,44]
#向队列中写入数据
for temp in data:
q.put(temp)
print(temp)
def analysis_data(q):
waitting_analysis_data=list()
#从对列中获取数据
while True:
data=q.get()
waitting_analysis_data.append(data)
if q.empty():
break
print(waitting_analysis_data)
def main():
#1.创建一个队列
q=multiprocessing.Queue()
#2.创建多个进程,将队列的引用当作实参进行传递里面
p1=multiprocessing.Process(target=download_from_web,args=(q,))
p2 = multiprocessing.Process(target=analysis_data,args=(q,))
p1.start()
p2.start()
pass
if __name__ == "__main__":
main()
迭代器_斐波那契数列
class Fibonacci(object):
def __init__(self,all_num):
self.all_num=all_num
self.current_num= 0
self.a = 0
self.b = 1
def __next__(self):
if self.current_num < self.all_num:
ret = self.a
self.a, self.b = self.b, self.a+self.b
self.current_num+=1
return ret
else:
raise StopIteration
def __iter__(self):
return self
fibo=Fibonacci(10)
for num in fibo:
print(num)
gevent实现多任务:
# jevent遇到延时就切换,没有则不切换
import gevent
import time
#f1,f2,f3可以用一个f就行了,下面这样写是为了方便理解
def f(n):
for i in range(n):
print(gevent.getcurrent(),i)
gevent.sleep(1)#必须使用gevent.sleep(),如果使用time.sleep()是不可以的,如果想用需要用gevent打补丁
# def f1(n):
# for i in range(n):
# print(gevent.getcurrent(),i)
# gevent.sleep(1)
# def f2(n):
# for i in range(n):
# print(gevent.getcurrent(),i)
# gevent.sleep(1)
# def f3(n):
# for i in range(n):
# print(gevent.getcurrent(),i)
# gevent.sleep(1)
#
g1=gevent.spawn(f,5)
g2=gevent.spawn(f,5)
g3=gevent.spawn(f,5)
g1.join()# 耗时
g2.join()
g3.join()
gevent打补丁
# jevent遇到延时(等待)就切换,没有则不切换
import gevent
import time
from gevent import monkey
monkey.patch_all()# 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块
#f1,f2,f3可以用一个f就行了,下面这样写是为了方便理解
def f(n):
for i in range(n):
print(gevent.getcurrent(),i)
time.sleep(1)#必须使用gevent.sleep(),如果使用time.sleep()是不可以的,如果想用需要用gevent打补丁
# g1=gevent.spawn(f,5)
# g2=gevent.spawn(f,5)
# g3=gevent.spawn(f,5)
# g1.join()# 耗时
# g2.join()
# g3.join()
# 15-20代码等价于下面代码:
gevent.joinall([gevent.spawn(f,5),gevent.spawn(f,5),gevent.spawn(f,5)])
多任务聊天器:
import socket
import threading
def rec_msg(udp_socket):
"""接受数据"""
while True:
recv_data=udp_socket.recvfrom(1024)
print(recv_data[0].decode("gbk"))
def send_msg(udp_socket,dest_ip,dest_port):
"""发送数据"""
while True:
send_data = input("请输入要发送的数据:")
udp_socket.sendto(send_data.encode("gbk"), (dest_ip, dest_port))
def main():
"""完成udp聊天器的整体控制"""
#1.创建套接字
udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#2.绑定本地信息
udp_socket.bind(("",7890))
#3.获取对方的Ip
# dest_ip=input("请输入对方的ip")
# dest_port=int(input("请输入对方的端口sort"))
dest_ip ="192.168.124.56"
dest_port=8080
#创建两个线程,去执行相应的功能
t_recv=threading.Thread(target=rec_msg,args=(udp_socket,))
s_send=threading.Thread(target=send_msg,args=(udp_socket, dest_ip,dest_port))
t_recv.start()
s_send.start()
if __name__ == "__main__":
main()
迭代图片下载器:
import urllib.request
import gevent
from gevent import monkey
monkey.patch_all()
def downloader(img_name,img_url):
req=urllib.request.urlopen(img_url)
img_content = req.read()
with open(img_name,"wb") as f:
f.write(img_content)
def main():
g1=gevent.spawn(downloader,"1.1.jpg","https://rpic.douyucdn.cn/asrpic/191118/6259669_4652236_0f7d3_2_2207.jpg")
g2=gevent.spawn(downloader,"2.2.jpg","https://rpic.douyucdn.cn/live-cover/roomCover/2019/11/15/ef6a286d4c6edb17b1ef6406fbc96e02_big.jpg")
g1.join()# 耗时
g2.join()
if __name__ == "__main__":
main()