"""======================1.tcp基本使用========================"""
# server端
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 9001))
sk.listen()
conn, addr = sk.accept()
res = conn.recv(1024)
print(res.decode("utf-8"))
conn.close()
sk.close()
# client端
import socket
sk = socket.socket()
sk.connect(("127.0.0.1", 9001))
sk.send("hello".encode("utf-8"))
sk.close()
"""=====================2.tcp循环发消息======================="""
# server端
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk.bind(("127.0.0.1", 9001))
sk.listen()
while True:
conn, addr = sk.accept()
while True:
res = conn.recv(1024)
print(res.decode())
strvar = input("服务端发送给客户端:")
conn.send(strvar.encode())
if strvar == "q":
break
conn.close()
sk.close()
# client端
import socket
sk = socket.socket()
sk.connect(("127.0.0.1", 9001))
while True:
strvar = input("客户端发送给服务端:")
sk.send(strvar.encode())
res = sk.recv(1024)
if res == b"q":
break
print(res.decode())
sk.close()
"""====================3.udp基本使用========================"""
# server端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(("127.0.0.1", 9001))
msg, cli_addr = recvfrom(1024)
print(msg.decode())
print(cli_addr)
msg = "hello"
sk.sendto(msg.encode(), cli_addr)
sk.close()
# client端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
mgs = "hi"
sk.sendto(msg.encode(), ("127.0.0.1", 9001))
msg, ser_addr = sk.recvfrom(1024)
print(msg.decode())
print(ser_addr)
sk.close()
"""====================4.解决tcp黏包========================"""
"""数据黏包是因为在client\server都会有一个数据缓冲区,用来临时保存数据,为了保证能够完整的接收数据,因此缓冲区都会设置的比较大.在收发数据频繁时,由于tcp传输消息的无边界,不清楚应该截取多少长度,导致client\server都有可能把多条数据当成是一条数据进行截取,造成黏包."""
"""黏包出现的两种情况:
1.在发送端,由于两个数据段,发送的时间间隔较短,所以在发送端形成黏包
2.在接收端,由于两个数据几乎同时被发送到对方缓存中,所以在接收端形成黏包
总之,发送时间间隔短,接收端接收不及时,就会造成黏包,主要是因为tcp对数据无边界截取,不会按照发送的顺序进行判断."""
# -------------------tcp及udp的黏包对比----------------------
"""
tcp:
优点:不限制数据报大小,稳定传输不丢包
缺点:接收数据无边界,有可能粘合几条数据成一条数据,造成黏包
udp:
优点:接收数据无边界,传输速度快,不会黏包
缺点:限制数据包的大小(受带宽路由器等因素影响),传输不稳定,可能会丢包
tcp发送失败时,还会选择再发,知道对方响应完毕为止;udp发送失败就失败了,不会再发,所以数据量过大时,容易丢包.
"""
# client端
# server端
"""====================5.udp循环发消息========================"""
# server端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(("127.0.0.1",9001))
# 收发消息逻辑
while True:
msg,cli_addr = sk.recvfrom(1024)
print(msg.decode("utf-8"))
print(cli_addr)
message = input("服务端向客户端发送消息:")
sk.sendto(message.encode("utf-8"),cli_addr)
sk.close()
# client端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
# 收发消息逻辑
while True:
message = input("客户端向服务端发送消息:")
sk.sendto(message.encode("utf-8"),("127.0.0.1",9000))
msg,addr = sk.recvfrom(1024)
print(msg.decode("utf-8"))
sk.close()
"""====================6.socketserver========================"""
"""
socketserver模块:
网络协议的最底层就是socket,基于原有socket模块,又封装了一层,就是socketserver
socketserver 为了实现tcp协议, server端的并发
"""
# server端
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
conn = self.request
while True:
msg = conn.recv(1024)
msg2 = msg.decode("utf-8")
print(msg2)
conn.send(msg2.upper().encode("utf-8"))
server = socketserver.ThreadingTCPserver(("127.0.0.1",9000), MyServer)
server.serve_forever()
client端
import socket
sk = socket.socket()
sk.connect(("127.0.0.1",9000))
while True:
sk.send(b"hello")
msg = sk.recv(1024)
print(msg.decode("utf-8"))
sk.close()
"""====================7.文件校验功能的实现========================"""
"""文件校验,我们用到了hashlib和hash,他们的区别是每次执行生成的字符串一个是固定的,一个是随机变化的,我们一般用固定的,也就是hashlib"""
# 1.针对小文件进行内容校验
import hashlib
# 定义一个函数,下面通过调用函数来实现功能,参数是两个文件
def check_md5(file):
with open(file, mode='rb') as fp: # 直接中rb模式读取文件内容字节流
hs = hashlib.md5() # 调用hashlib模块中的md5方法进行加密,生成一个加密对象hs
hs.update(fp.read()) # 读取是是字符串,把从文件中读取的内容更新到hs对象中
return hs.hexdigest() # 然后返回hs对象的加密后的32位的16进制字符串
res1 = check_md5("ceshi1.txt") # 操作的是字节流
res2 = check_md5("ceshi2.txt")
print(res1, res2) # 5da6e36fa3a3ac23e3d25b107fcd1069 32位16进制
print(type(res1)) # str
# -----------------------------------------------------
# 2.针对大文件的内容校验
"""大文件和小文件的校验原理相同,分批加密和一次性加密的结果是一样的,如果是大文件的话,我们通常采用分批读取的方式,也有两种方法可以实现校验
加密三部曲: hashlib.md5 -> update() -> hexdigest ,都是基于一个hs对象
"""
# 方法1: 通过一行行的读取内容,直到文件读取完毕,加密完毕
import hashlib
def check_md5(file):
hs = hashlib.md5()
with open(file,mode='rb') as fp:
while True:
content = fp.read(10) # 一次最多读取10个字节
if content: # 根据内容进行计算,如果大文件中没内容了就break
hs.update(content)
else:
break
return hs.hexdigest() # 返回hs对象的加密后的32位的16进制字符串
print(check_md5("ceshi1.txt"))
print(check_md5("ceshi2.txt")) # 两个文本差一行空格,结果都会不一样
# 方法2: 通过每次都读取相同的字节数,直到读完
import hashlib, os
def check_md5(file):
hs = hashlib.md5()
file_size = os.path.getsize(file) # 利用os模块计算文件大小
with open(file, mode='rb') as fp:
while file_size: # 当file_size还为真,就继续循环
content = fp.read(10) # 一次就读10个字节
hs.update(content)
file_size -= len(content) # 文件总大小减去已经读取的大小
return hs.hexdigest()
print(check_md5("ceshi1.txt"))
print(check_md5("ceshi2.txt"))
"""====================8.服务器合法性校验功能的实现========================"""
# server端(支付宝)
import socket,hmac,os
def auth(conn,secret_key):
msg = os.urandom(32) # 随机产生32位二进制字节流 os.urandom()
conn.send(msg)
hm = hmac.new(secret_key.encode(),msg) # 把秘钥加密成一个32位二进制字节流,作为hm对象
res_serve = hm.hexdigest() # 返回一个16进制32位的字符串
print(type(res_serve))
res_client = conn.recv(1024).decode("utf-8") # 将从客户端接收的秘钥信息解码
if res_client == res_serve: # 通过秘钥验证用户合法性
print("是合法用户")
return True
else:
print("非法用户")
return False
sk = socket.socket() # 以下就是tcp的那一套,多了一个设置秘钥以及判断结果的过程
sk.bind(("127.0.0.1",9000))
sk.listen()
conn,addr = sk.accept()
# 设置秘钥
secret_key = 'nbnb'
res = auth(conn,secret_key) # 把对象conn和秘钥作为函数参数传到auth里,完成服务器合法性校验,先看看auth函数中有什么小动作
if res: # 如果res能对上,那么说明服务器合法,该传数据再传数据
print(conn.recv(1024).decode("utf-8"))
conn.close()
sk.close()
# client端(公司)
import socket,hmac,hashlib
def auth(sk,secret_key):
# 处理权限验证的逻辑
msg = sk.recv(32)
hm = hmac.new(secret_key.encode(),msg)
res = hm.hexdigest()
print(res)
sk.send(res.encode("utf-8"))
sk = socket.socket()
sk.connect(("127.0.0.1",9000))
secret_key = "nbnb"
auth(sk,secret_key)
sk.close()
"""====================9.tcp实现用户登录========================"""
"""
模拟tcp协议,做一个登录
1.2个文件 ,客户端和服务端
2.依照文件中的账户和加密过的密码,比对客户端和服务端的用户信息的一致性.
3.如果一致显示登录成功,否则失败断开连接.
"""
# server端
import hashlib,socket,json
# 定义一个加密函数
def get_md5_code(usr,pwd):
hs = hashlib.md5(usr.encode()) # 把用户名用md5加密
hs.update(pwd.encode()) # 把密码和用户名混合在一起进行加密,形成32位字节流
return hs.hexdigest() # 返回一个32位16进制字符串
sk = socket.socket()
sk.bind(("127.0.0.1",9001))
sk.listen()
# 三次握手
conn,addr = sk.accept()
# 收发数据流程
msg = conn.recv(1024).decode()
# 把收到的字符串回复称原来的字典
dic = json.loads(msg)
print(dic)
sign = False
with open("userinfo.txt",mode='r',encoding='utf-8') as fp:
for line in fp:
usr,pwd = line.strip().split(":")
print(usr,pwd)
# 账号和密码如果比对成功,直接终止循环,该sign标记为True
if usr == dic["username"] and pwd == get_md5_code(dic["username"],dic["password"]):
# 发送状态码1代表成功
res = {"code":1}
res_msg = json.dumps(res).encode()
conn.send(res_msg)
sign = True
break
# 通过最后的循环,发现sign标记仍然是False,代表账号和密码是错的
if sign == False:
res = {"code":0}
res_msg = json.dumps(res).encode()
conn.send(res_msg)
conn.close()
sk.close()
# client端
import socket,json
sk = socket.socket()
sk.connect(("127.0.0.1",9001))
# 收发数据的逻辑
usr = input("请输入用户名:")
pwd = input("请输入密码;")
# 封装好要发送的数据
dic = {"username":usr,"password":pwd,"operate":"login"}
# 通过json编程字符串
res = json.dumps(dic)
# 把字符串变成字节流
bytes_msg = res.encode()
# 发送字节流
sk.send(bytes_msg)
# 接收服务端发送过来的数据
res_msg = sk.recv(1024).decode()
dic_code = json.loads(res_msg)
if dic_code["code"]:
print("success")
else:
print("fail")
sk.close()
"""====================10.进程========================"""
"""
1.进程:正在运行的程序就是进程,进程是操作系统中资源分配的最小单位
进程与进程之间:数据彼此隔离,通过socket通信.同一个程序执行两次就是两个进程
2.并行与并发:1个cpu不停的执行多个程序;多个cpu不停的执行多个程序
3.cpu进程调度:fcfs\短作业优先\时间片轮转\多级反馈队列(越是时间常常的,cpu分配的资源越短,优先级越靠后)
4.进程的三状态:就绪态(Ready)\执行态(Running)\阻塞态(Blocked)
5.同步\异步\阻塞\非阻塞:一条主线是同步,多条主线是异步; 异步非阻塞效率最高,cpu最热
"""
# 创建一个带有参数的进程
from multiprocessing import Process
import os
def func(n):
for i in range(1,n+1):
print("子进程id{},父进程id{}".format(os.getpid(),os.getppid())) # 子进程id14881,父进程id14880 , 5个一样
if __name__ == "__main__":
print("子进程id{}啊,父进程id{}啊".format(os.getpid(), os.getppid())) # 子进程id14880啊,父进程id1650啊
n = 5
# 创建子进程, Process创建子进程,返回进程的对象p; target指定要执行的任务; args指定传递的参数,元组类型,逗号隔开
p = Process(target=func,args=(n,))
p.start()
for i in range(1,n+1):
print("*" * i)
# ----------------------------------------
# 进程之间数据隔离的证明过程
from multiprocessing import Process
import time
count = 100
def func():
global count
count += 1
print("我是子进程count={}".format(count)) # 101
if __name__ == "__main__":
p = Process(target=func)
p.start()
time.sleep(1) # 主进程阻塞1秒,所以子进程先执行完毕.子进程输出101
print(count) # 100 并没有受到子进程影响,证明进程间的数据隔离,但是底层通过socket通信,这并不冲突
# ------------------------------------------
# 多个进程可以异步并发,子父进程之间的关系
"""因为cpu调度策略问题,进程不一定先执行谁后执行谁,整体而言,主进程速度快于子进程
主进程默认等待所有子进程执行结束后,再关闭程序,释放资源
如果不等待,子进程不方便管理,且容易造成僵尸进程,在后台不停的占用系统资源
什么是僵尸进程呢,就是如果主进程执行完毕之后,不等待子进程直接关闭程序,那么会有一部分未执行完成的子进程永远没办法执行完毕,变成僵尸"""
from multiprocessing import Process
import os
def func(args):
print("子进程id{},父进程id{}".format(os.getpid(),os.getppid()))
if __name__ == "__main__":
for i in range(1,11):
Process(target=func,args=(i,)).start() # 建立10个子进程
print("主进程执行完毕..")
# ---------------------------------------------------
"""因为cpu调度策略问题,主进程会快于子进程优先执行完毕,但我们往往希望,子进程最后执行,那么我们可以使用join()和守护进程来实现我们的期望"""
# 多个子进程配合join使用
from multiprocessing import Process
import os
def func(args):
print("第%s个人" % (args))
if __name__ == "__main__":
lst = []
for i in range(1,11):
p =Process(target=func,args=(i,))
print(p)
lst.append(p) # 把这些子进程对象p统一放进一个列表里,一会统一join,这样join一次就可以实现我们的需求
# join()应该加在哪里?
for i in lst:
# print(i) # <Process(Process-10, started)> 这里的其实就是p,p是一个子进程对象
i.join()
print("主进程执行完毕")
# -------------------------------------------------------
# 使用自定义类的方式创建进程
"""
使用自定义类创建进程的要求:
1.必须继承Process这个类
2.所有进程执行任务的逻辑要写到run方法里
"""
# 使用自定义类创建一个带有参数的进程
from multiprocessing import Process
import os
class MyProcess(Process):
def __init__(self,arg):
# 必须手动调用一下父类的构造方法,实现进程的创建,没有这句就会报错,错误如下:
super().__init__() # AttributeError: 'MyProcess' object has no attribute '_popen'
self.arg = arg
def run(self):
print("子进程id{}啊,父进程id{}啊".format(os.getpid(),os.getppid()),self.arg)
if __name__ == "__main__":
MyProcess("xboy").start()
print("子进程id{},父进程id{}".format(os.getpid(),os.getppid()))
"""====================11.守护进程========================"""
"""
1.守护进程:守护的是主进程,如果主进程执行结束了,意味着守护进程的寿命立刻终止,立刻杀死
2. p.daemon = True 设置当前进程为守护进程,必须卸载start()调用进程之前进行设置
3.默认情况下,主进程会等待所有子进程执行完毕后,关闭程序,释放资源.守护进程再主进程代码执行结束之后,直接杀掉所有没执行完的子进程
4.可以给子进程贴上守护进程的名字,该进程会随着主进程代码执行完毕而结束(为主进程守护)
(1)守护进程会在主进程代码执行结束后就终止
(2)守护进程内无法再开启子进程,否则抛出异常(了解)
"""
# 守护进程之多子进程场景
from multiprocessing import Process
import time
def func1():
count = 1
while count <= 10:
print("*" * count)
time.sleep(0.5)
count += 1
def func2():
print("start func2")
time.sleep(2)
print("end func2")
if __name__ == "__main__":
p1 = Process(target=func1)
p2 = Process(target=func2)
# 如果没有这一行,虽然主程序是第一个执行完毕的,他也不会干掉子进程,而是会等待执行完毕
p1.daemon = True # 设置里p1守护进程,在主程序执行完毕之后,直接干掉p1
p1.start()
p2.start()
time.sleep(2) # 这里给主进程加一个2秒阻塞,可以多给子进程一些执行时间,就能明显看出到底是干掉了谁
print("主程序代码执行结束..")
# -------------------------------------------------
"""守护进程有什么用呢? 可以用来监控报活"""
from multiprocessing import Process
import time
# 监控报活
def alive():
while True:
print("给监控服务器发消息,当前5号服务器功能正常,i'm ok")
time.sleep(5)
# 当前服务器正常完成的功能
def func():
while True:
time.sleep(10)
print("当前5号服务器功能,统计财务报表")
if __name__ == "__main__":
p1 = Process(target=alive)
p2 = Process(target=func)
p1.daemon = True # 设置alive守护进程,待主进程执行完毕后,直接干掉p1子进程
p1.start()
p2.start()
p2.join() # 等待p2进程执行结束之后,下面的主程序代码才会运行
print("当前服务器状态:统计财务报表功能异常")
"""
alive为监控进程,每5秒打印一个自己的状态; func为正常进程,每10秒打印一个状态,因为设置了alive为守护进程,当主进程执行完毕,直接干掉alive.
由于func.join(),那么,之后等待func执行完毕之后,才能运行主进程,才能干掉alive.
如果有一天func不正常了,那么主程序执行完就会干掉alive,这个程序就停下来了,我们可以直接冲着func去检查故障.
这就起到了监控报活的功能
"""
"""====================12.锁(Lock)和信号量(Semaphore)========================"""
"""
1.同一时间,允许一个进程上一把锁,就是Lock
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是满了,但为了保证数据安全,我们只能牺牲一部分速度
2.同一时间允许多个进程上多把锁,就是信号量[Semaphore]
信号量是锁的变形,实际实现是 计数器 + 锁. 同时允许多个进程上锁
3.互斥锁Lock:互斥锁就是进程互相排斥,谁先抢到资源,谁就上锁改变资源内容,为了保证数据的同步性
注意一点:多个锁一起上,不开锁,就会造成死锁.上锁和解锁永远是一对
lock.acquire() lock.release()
"""
# 模拟12306抢票软件
from multiprocessing import Process,Lock
import json,time
# 读取票数,更新票数
def wr_info(sign,dic=None):
if sign == "r":
with open("ticket",mode="r",encoding="utf-8") as fp:
dic = json.load(fp)
# print(dic) # {'count':6}
return dic
elif sign == "w":
with open("ticket",mode="w",encoding="utf-8") as fp:
json.dump(dic,fp)
# 抢票方法
def get_ticket(person):
dic = wr_info("r")
time.sleep(0.5)
if dic["count"] > 0:
print("%s抢到票了" % (person))
dic["count"] -= 1
wr_info("w",dic)
else:
print("%s没有抢到票" % (person))
def run(person,lock):
dic = wr_info("r")
print("%s 查询票数: %s" % (person,dic["count"]))
lock.acquire() # 上锁,get_ticket()有一个人执行完之后,再分配另一个人来执行
get_ticket(person) # 因为上锁,当执行get_ticket这个方法的时候,各个进程之间是同步的
lock.release() # 解锁
if __name__ == "__main__":
lock = Lock() # 主进程中定义一个lock的对象
# print(type(lock)) # <class 'multiprocessing.synchronize.Lock'>
lst = ["刘思敏7","云超1","张恒2","尉翼麟3","王振4","黎建忠5","刘鑫炜6","李天兆8","魏小林9","李博10"]
for i in lst: # 从列表中拿人,分别创建子进程,去执行run函数,参数是人名和锁,所以run函数的参数必须有person和lock
p = Process(target=run,args=(i,lock)) # 创建10个进程的时候是异步的,知道查询完票数为止,因为查询完票数之后,程序开始上锁了
p.start()
# ----------------------------------------------------------
# Semaphore 信号量,多把锁, 可以控制上锁的数量,同一时间最多允许几个进程上锁,创建进程的时候是异步的,执行任务的时候是同步的
from multiprocessing import Process,Semaphore
import time,random
def ktv(person,sem):
sem.acquire()
print("%s进入,开始唱歌" % (person))
time.sleep(random.randrange(3,7)) # 随机生成一个3,4,5,6的阻塞
print("%s离开,唱完了" % (person))
sem.release()
if __name__ == "__main__":
sem = Semaphore(4) # 限制上锁数量为4,生成一个对象sem
for i in range(10):
Process(target=ktv,args=("person%s" % (i),sem)).start()
"""====================13.Event事件========================"""
"""
e = Event() 生成时间对象e
e.wait() 动态给程序加阻塞
e.set() 将属性值改为True
e.clear() 将属性值改为False
e.is_set() 判断当前的属性是否为True , 默认上来是False, 如果是True,不加阻塞;如果是False,加阻塞
"""
# 模拟红绿灯功能
from multiprocessing import Process,Event
import time,random
def traffic_light(e):
print("红灯亮")
while True:
if e.is_set():
time.sleep(1)
print("红灯亮")
e.clear() # 将属性值改为False
else:
time.sleep(1)
print("绿灯亮")
e.set() # 将属性值改为True
def car(e,i):
if not e.is_set():
print("car%s在等待" % (i))
e.wait() # 动态给e加阻塞
print("car%s通行了" % (i) )
# if __name__ == "__main__":
# e = Event()
# p1 = Process(target=traffic_light,args=(e,))
# p1.start()
#
# for i in range(1,21):
# time.sleep(random.randrange(0,2))
# p2 = Process(target=car,args=(e,i))
# p2.start()
# 红绿灯改造(跑完小车后,炸掉红绿灯)-----------------------------------------
if __name__ == "__main__":
lst = []
e = Event()
p1 = Process(target=traffic_light,args=(e,))
p1.daemon = True
p1.start()
for i in range(1,21):
time.sleep(random.randrange(0,2))
p2 = Process(target=car,args=(e,i))
p2.start()
lst.append(p2)
for i in lst:
i.join()
print("程序执行结束")
"""====================14.进程队列(Queue)========================"""
"""
1.基本原则:先进先出,后进后出
2.put:存 get:取
3.当队列中没有数据时,再调用get会发生阻塞
4.get_nowait() 存在兼容性问题,windows好用,linux不好用,不推荐使用
5.可以限定Queue队列的长度 q = Queue(3), 当列表已经满了,再往里添加数据会直接报错
6.进程之间通过队列交换数据
"""
from multiprocessing import Process,Queue
import queue # Queue进程队列 queue线程队列
def func(q):
res = q.get()
print(res)
q.put("xboy")
res = q.get()
print(res)
if __name__ == "__main__":
q = Queue()
p = Process(target=func,args=(q,))
p.start()
q.put("alex")
# 为了等待子进程把数据塞到队列中再获取,要加一个join
p.join()
res = q.get() # 此时q中已经没有值了,调用get会发生阻塞,但不会报错
print(res)
print("主程序执行完毕..")
"""====================15.生产者消费者模型========================"""
# 理想的生产者消费者模型,追求彼此的速度相对均匀;从程序上来说,生产者负责存储数据,消费者负责获取数据
from multiprocessing import Process,Queue
import random,time
def consumer(q,name):
while True:
food = q.get() # 从进程队列Queue里拿数据
if food is None: # 消费者模型如果获取的是None,则停止消费
break
time.sleep(random.uniform(0.1,1)) # 利用random.uniform()在0.1-1的范围内随机产生一个小数
print("%s消费了%s" % (name,food))
def producer(q,name,food):
for i in range(1,6):
time.sleep(random.uniform(0.1,1))
print("%s生产了%s,%s" % (name,food,i))
q.put(food+str(i)) # 把数据放入进程队列Queue中,同时利用了字符串拼接,显示food的数量
if __name__ == "__main__":
q = Queue()
p1 = Process(target=producer,args=(q,"xboy","rice"))
p1.start()
p2 = Process(target=producer,args=(q,"alex","soup"))
p2.start()
c1 = Process(target=consumer,args=(q,"dagou"))
c1.start()
c2 = Process(target=consumer,args=(q,"ergou"))
c2.start()
# 在生产完所有的数据之后,在队列的末尾塞入一个None
p1.join()
p2.join()
q.put(None)
q.put(None)
"""======================16.JoinableQueue改造生产者消费者模型========================"""
"""
task_done 队列计数-1
task_done 配合 join一起使用, join函数,会根据队列中的计数器来判定时阻塞还是放行,如果计数器变量是0,意味着放行,其他情况阻塞
"""
基本使用
from multiprocessing import Process,JoinableQueue
jq = JoinableQueue()
jq.put("a")
# jq.put("b")
# print(jq.get())
jq.task_done()
jq.join() # join判断jq中变量是否为0,为0放行,其他阻塞
print("finish")
# 改造生产者消费者模型
from multiprocessing import Process,JoinableQueue
import random,time
def consumer(jq,name):
while True:
food = jq.get()
time.sleep(random.uniform(0.1,1))
print("%s消费了%s" % (name,food))
jq.task_done()
def producer(jq,name,food):
for i in range(1,5):
time.sleep(random.uniform(0.1,1))
print("%s生产了%s%s" % (name,food,i))
jq.put(food+str(i))
if __name__ == "__main__":
jq = JoinableQueue()
p1 = Process(target=producer,args=(jq,"xboy","rice"))
# p1.daemon = True
p1.start()
# p2 = Process(target=producer,args=(jq,"xgril","soup"))
# # p2.daemon = True
# p2.start()
c1 = Process(target=consumer,args=(jq,"alex"))
c1.start()
p1.join() # 把生产者所有的数据都装在到队列中
jq.join() # 当队列计数器减到0时,立刻放行.必须等待消费者模型中,所有的数据都task_done之后,才放行
print("主程序执行完毕...")
"""======================17.Manager========================"""
# Manager(list列表,dict字典), 进程之间的共享数据
from multiprocessing import Process,Manager,Lock
def work(data,lock):
with lock: # 使用with语法可以简化上锁解锁的过程,直接with lock ,后面写逻辑即可
# data[0] += 1
data["count"] -= 1
if __name__ == "__main__":
# 创建一个Manager对象m
m = Manager()
# 创建一个共享的字典
data = m.dict({"count":2000})
# 创建一个共享的列表
# data = m.list([1,2,3])
lst = [] # 创建一个空列表,把子进程都放进去,最后方便统一join
lock = Lock()
for i in range(100):
p = Process(target=work,args=(data,lock))
p.start()
lst.append(p)
for i in lst: # join,所有子进程都执行完毕后,再执行主进程
i.join()
print(data) # [101,2,3]
print(data) # {'count':1900}
"""======================18.线程========================"""
"""
进程是资源分配的最小单位,线程是计算机中调度的最小单位.进程是一片地,线程是地上干活的人
线程是比较轻量级,能干更多的活,一个进程中所有线程资源是共享的,一个进程中至少有一个线程在工作
并发多线程和多进程,多线程的速度更快
"""
from threading import Thread
from multiprocessing import Process
import os
def func(num):
print("当前线程{},所归属的进程id号是{}".format(os.getpid(),num))
for i in range(10):
t = Thread(target=func,args=(i,))
t.start()
print(os.getpid())
# -------------------------------------------------------
from threading import Thread
from multiprocessing import Process
import os,time
def func(num):
print("{},{}".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("运行时间:{}".format(endtime-starttime)) # 0.6948s
# -------------------------------------------------------
from threading import Thread
from multiprocessing import Process
import os,time
def func(num):
print("{},{}".format(os.getpid(),num))
if __name__ == "__main__":
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("运行时间:{}".format(endtime-starttime)) # 3.8603s
-------------------------------------------------------
# 多线程,共享同一份进程资源
from threading import Thread
from multiprocessing import Process
import os,time
num = 1000
def func():
global num
num -= 1
print(num)
for i in range(1000):
t = Thread(target=func)
t.start()
print(num)
"""======================19.用类定义线程及线程相关函数========================"""
# 用类自定义线程
from threading import Thread,currentThread
import time,os
class MyThread(Thread):
def __init__(self,name):
super().__init__() # 手动调用父类的构造方法
self.name = name
def run(self): # 逻辑必须写在run方法里
time.sleep(1)
print("当前进程正在执行running..",self.name)
if __name__ == "__main__":
t = MyThread("hello")
t.start()
t.join()
print("主进程执行结束...")
"""
线程相关函数:
1. 线程.is_alive() 检测线程是否仍然存在
2. 线程.setName() 设置线程名字
3. 线程.getName() 获取线程名字
4. currentThread().ident 查看线程id号
5. enumerate() 返回目前正在运行的线程列表
6. activeCount() 返回目前正在运行的线程数量
"""
"""======================20.守护线程========================"""
# 守护线程:等待所有线程全部执行完毕之后,再自己终止.守护的是所有线程.这一点跟守护进程还是有些区别的
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)
# t1会正常运行,直到t2运行完毕,所有线程都运行完了,t1被干掉
t1.setDaemon(True) # 设置守护线程,setDaemon(),括号里面要写上True,同样要写道start()前面
t1.start()
t2.start()
t2.join() # 通过join()设置阻塞,当t2执行完之后,再执行主线程
print("主线程执行结束..")
"""======================21.Lock锁与信号量[Semaphore]========================"""
# Lock 保证线程数据安全
from threading import Lock,Thread
import time
n = 0
def func1(lock):
global n
lock.acquire()
for i in range(1000000):
n -= 1
lock.release()
def func2(lock):
global n
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) # 主线程执行结束.. 0 1.2756810188293457
# ----------------------------------------------------------------------
# 信号量 Semaphore 线程的信号量,允许上多把锁
"""在创建线程的时候是异步创建,在执行任务的时候,因为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()
"""======================22.死锁\互斥锁\递归锁========================"""
"""
加一把锁,就对应解一把锁,形成互斥锁;从语法上来说,锁可以互相嵌套,但不推荐使用
不要因为逻辑问题让上锁分为两次,从而导致死锁
递归锁用于解决死锁,但只是一种应急的处理办法.临时用于快速解决项目因死锁问题而不能正常运行的场景
"""
# 递归锁
from threading import Lock,Thread,RLock
import time
rlock = RLock() # 递归锁
lock = Lock() # 互斥锁
def func():
rlock.acquire() # 连续加了两把锁,并没有影响到print打印,这就是递归锁的作用.如果是互斥锁,此时已经报错
rlock.acquire()
print(111)
rlock.release()
rlock.release()
print(222)
func()
# 用递归锁来解决死锁
# lock1 = lock2 = RLock() # 使两个锁都等于递归锁
# 互斥锁
# 尽量使用一把锁解决问题,不要互相嵌套,否则容易造成死锁
"""======================23.事件Event========================"""
# 与进程事件Event用法一致
# 通过线程Event模拟连接远程数据库
from threading import Event,Thread
import random,time
# 若连接三次数据库,都不成功,则直接抛出异常
def check(e):
time.sleep(random.randrange(1,6))
print("开始检测链接合法性")
e.set() # 将阻塞事件的值改为True
def connect(e):
sign = False
for i in range(1,4): # 最多尝试连接3次
# 设置最大等待时间为1秒
e.wait(1)
if e.is_set(): # 获取阻塞事件的值
print("数据库连接成功..")
sign = True
break
else:
print("尝试连接数据库%s次失败" % (i))
if sign == False:
raise TimeoutError # 主动抛出异常
e = Event()
Thread(target=check,args=(e,)).start()
Thread(target=connect,args=(e,)).start()
"""======================24.线程队列========================"""
"""
put get
put_nowait() # 存,超出了队列长度,报错
get_nowait() # 取,没数据了取不出来,报错
linux, windows 线程中put_nowait,get_nowait都支持
1.Queue 先进先出,后进后出
2.LifoQueue 先进后出,后进先出
3.PriorityQueue 按照优先级顺序排序,默认从小到大排序.要么全是数字,要么诠释字符串,不能混合,否则报错;如果队列中是元组,那么默认按照元组中的第一个元素进行排序
"""
"""======================25.进程池和线程池========================"""
"""
# 线程池
# 实例化线程池 ThreadPoolExcutor (推荐5*cpu_count)
# 异步提交任务 submit / map
# 阻塞直到任务完成 shutdown
# 获取子线程的返回值 result
# 使用回调函数 add_done_callback
# 回调函数
就是一个参数,将这个函数作为参数传到另一个函数里面.
函数先执行,再执行当参数传递的这个函数,这个参数函数是回调函数
# 线程池 是由子线程实现的
# 进程池 是由主进程实现的
"""
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import os,time
def func(i):
print("start..",os.getpid())
time.sleep(10)
print("end..",i)
return i
# ProcessPoolExecutor 进程池基本使用
if __name__ == "__main__":
lst = []
print(os.cpu_count()) # os.cpu_count() 显示cpu逻辑核心数
p = ProcessPoolExecutor() # 创建进程池对象p
for i in range(10):
res = p.submit(func,i) # 异步提交任务
lst.append(res)
for i in lst:
print(i.result()) # 通过result()获取当前进程池返回值
p.shutdown() # join, 等待所有子进程执行结束
print("主进程执行结束...")
# -----------------------------------------------------------
# ThreadPoolExecutor 线程池的基本用法
from threading import current_thread as cthread
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import os,time
def func(i):
print("thread..start",cthread().ident,i)
time.sleep(3)
print("thread..end",i)
return cthread().ident # 不管是进程池还是线程池,都可以提取到返回值,这是普通的进程或者线程做不到的,更加推荐使用进程池和线程池
if __name__ == "__main__":
lst = [] # 异步多线程通过往空列表里传值的方法,最后统一设置阻塞,调用shutdown,保证主程序最后执行,这样效率最高
setvar = set()
tp = ThreadPoolExecutor() # 4个逻辑cpu,4*5=20个线程并发
for i in range(100):
res = tp.submit(func,i) # 异步提交任务,参数是函数及其参数
lst.append(res)
for i in lst:
setvar.add(i.result()) # 通过result()获取func返回值,是线程id号,通过add()添加到集合中
tp.shutdown() # 等待所有子进程执行结束, shutdown()
print(len(setvar), setvar) # setvar长度为20,里面有4*5=20个进程id
print("主线程执行结束..")
# -----------------------------------------------------------
# 线程池map
from concurrent.futures import ThreadPoolExecutor
from threading import current_thread as cthread
from collections import Iterator
import time
def func(i):
time.sleep(0.5)
return "*" * i
if __name__ == "__main__":
setvar = set()
lst = []
tp = ThreadPoolExecutor(5)
it = tp.map(func,range(20))
print(isinstance(it,Iterator))
tp.shutdown()
for i in it:
print(i)
"""======================26.回调函数========================"""
"""
1.回调函数:把函数当成参数传递给另外一个函数,在当前函数执行完毕之后,最后调用一下该参数(函数),这个函数就是回调函数
2.功能:
打印状态: a属性
支付状态: b属性
退款状态: c属性
转账的状态: d属性
把想要的相关成员或者相关逻辑写在自定义的函数中,支付宝接口在正常执行之后,会调用自定义的函数,来执行相应的逻辑
"""
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from threading import current_thread as cthread
import os,time
def func1(i):
print("Process start...",os.getpid())
time.sleep(0.5)
print("Process end...",i)
return "*" * i
def func2(i):
print("thread strat...",cthread().ident)
time.sleep(0.5)
print("thread end..",i)
return "*" * i
def call_back1(obj):
print("回调函数callback进程号",os.getpid())
print(obj.result())
def call_back2(obj):
print("回调函数callback线程号",cthread().ident)
print(obj.result())
if __name__ == "__main__":
p = ProcessPoolExecutor()
for i in range(1,11):
res = p.submit(func1,i)
res.add_done_callback(call_back1)
p.shutdown()
print("主进程执行结束..",os.getpid())
python_learn 6.12-6.13学习总结
最新推荐文章于 2024-06-18 14:09:13 发布