python_learn 6.12-6.13学习总结

"""======================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())

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值