1.意义:充分利用计算机多核资源,提高程序的运行效率。
2.实现方案:多进程,多任务。
3.并发和并行:
2.系统的集成是如果产生的——产生过程
3.进程基本概念
status | 描述 |
---|---|
S | 等待态 |
R | 执行态 |
D | 等待态 |
T | 等待态 |
Z | 僵尸态 |
status状态后面有的字母代表的意思:
符号 | 示意 |
---|---|
< | 有较高优先级 |
N | 优先级较低 |
+ | 前台进程(在终端有现象显示的) |
s | 会话组组长 |
l | 有多线程的 |
面试要求:
1.什么是进程?进程和程序之间有什么区别?
2.进程有哪些状态?状态之间如何转化?
(系统层级的编程接口,在Linux或者Unix下进行)
###4.3.1fork的使用
pid = os.fork()
功能:创建新的进程
返回值:整数,如果进程创建失败则返回负数,如果创建成功,则在原有进程中返回新进程的PID,在新进程中返回0
#fork函数演示
import os
pid = os.fork()
if pid < 0:#创建进程失败
print("Create process failed")
elif pid == 0:#成功创建子进程
print("The new prcess is created")
else:#父进程
print("The old process")
print("fork test over")
注意:
AttributeError: module 'os' has no attribute 'fork'
函数名 | 返回值 | 功能 |
---|---|---|
os.getpid() | 返回当前进程的pid | 获取一个进程的pid |
os.getppid() | 返回父进程的pid | 获取父进程的pid号 |
os._exit(status) | 参数:进程的终止条件 | 结束一个进程 |
sys.exit([status]) | 参数:整数 表示退出状态,字符串 表示退出时打印内容 | 退出进程 |
1.孤儿进程:父进程先与子进程退出,此时子进程成为孤儿进程。
特点:孤儿进程会被系统进程收养,此时系统进程就会成为孤儿进程新的父进程,孤儿进程退出该进程会自动处理。
2.僵尸进程:子进程先于父进程退出,父进程又没有处理子进程的退出状态,此时子进程就被成为僵尸进程。
特点:僵尸进程虽然结束,但是会存留部分PCB在内存中,大量的僵尸内存会浪费系统的内存资源。
3.如何避免僵尸进程产生
"""
创建二级子进程,放置僵尸进程
"""
import os
import time
def fun01():
for i in range(4):
time.sleep(2)
print("写代码····")
def fun02():
for i in range(5):
time.sleep(1)
print("测试代码····")
pdidd = os.fork()
if pdidd < 0:
print("Error")
elif pdidd == 0:
p = os.fork()#二级子进程
if p == 0:
fun02()
else:
os._exit(0)
else:
os.wait()
fun01()
import signal
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
特点:非阻塞,不会影响父进程运行,可以处理所有子进程退出。
功能:类似于QQ群功能
思路:
1.技术点
2.结构设计
3.分析功能模块,制定编写流程
搭建网络连接
进入聊天室
客户端:
服务端:
聊天
客户端
退出聊天室
管理员消息
4.协议
如果允许进入聊天室,服务端发送OK给客户端。
如果不允许进入聊天室,服务端发送不允许的原因。
请求类别:
客户端如果输入quit或者ctrl-c,或者esc表示退出
用户存储结构:{name:addr,…}
代码示例:(完整代码)
注意:因为使用fork函数,win下不行,必须在Linux或者Unix下运行,(同时这里并没有图像界面,所以这里只是在终端运行,测试收发消息。)
"""
群聊天服务端
"""
import socket,os,sys
ADDR = ('0.0.0.0',10006)
user = {}
def do_login(s,name,addr):
if name in user or "管理员" in name:
s.sendto("该用户已经存在".encode(),addr)
return
s.sendto(b'OK',addr)
#通知其他人
msg = "欢迎%s进入聊天室!"%name
for i in user:
s.sendto(msg.encode(),user[i])
#将用户加入
user[name] = addr
def do_chat(s,name,text):
"""
聊天
:param s:socket套接字对象
:param name:姓名
:param text:聊天内容
:return:
"""
msg = "%s : %s"%(name,text)
for i in user:
if i != name:
s.sendto(msg.encode(),user[i])
def do_quit(s,name):
"""
服务端退出聊天室
:param s:
:param name:
:return:
"""
msg = "%s退出聊天室"%name
for i in user:
if i != name:
s.sendto(msg.encode(),user[i])
else:
s.sendto(b'EXIT',user[i])
#将用户删除
del user[name]
def do_request(sockfd):
"""
处理请求
:param sockfd:
:return:
"""
while True:
data,addr = sockfd.recvfrom(4029)
# print(data.decode())
msg = data.decode().split(' ')
#区分请求类别
if msg[0] == 'L':
do_login(sockfd,msg[1],addr)
elif msg[0] == 'C':
text = ' '.join(msg[2:])
do_chat(sockfd,msg[1],text)
elif msg[0] == 'Q':
do_quit(sockfd,msg[1])
#创建网络连接
def main():
#套接字
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sockfd.bind(ADDR)
pid = os.fork()
if pid < 0:
return
elif pid == 0:#子进程专门用来发送管理员消息
"""
注意:由于创建新进程,同时会创建父进程中所有的数据,当然也是会创建user的数据
但是,所有添加用户都是在父进程中执行的,所以子进程中的user是一直为空。
因为子进程要将管理员消息发送给所有用户,所以?
可以考虑,服务端将管理员消息发送给服务端,即发送给自己,通过协议C表示聊天信息,
让子进程发送的消息发送给父进程,让父进程去处理。
还需要考虑的是:因为是子进程发送给父进程,父进程判断name为“管理员消息”,如果
user表中已存在“管理员消息”这个用户该怎么办?
"""
while True:
msg = input("管理员消息:")
msg = "C 管理员消息 " + msg
sockfd.sendto(msg.encode(),ADDR)
else:
#请求处理
do_request(sockfd)#父进程专门处理客户端请求
if __name__ == '__main__':
main()
"""
群聊天客户端
"""
import socket,os,sys
ADDR = ('127.0.0.1',10006)
def send_msg(s,name):
"""
发送消息
:param s:socket套接字对象
:param name:发送者姓名
:return:
"""
while True:
try:
text = input("发言:")
except KeyboardInterrupt:
text = 'quit'
if text == 'quit':#表示退出聊天室
msg = "Q "+ name
s.sendto(msg.encode(),ADDR)
sys.exit("退出聊天室")
msg = "C %s %s"%(name,text)#定义协议C:发言内容
s.sendto(msg.encode(),ADDR)
def recv_msg(s):
"""
接受消息
:param s: socket套接字对象
:return:
"""
while True:
data,addr = s.recvfrom(4096)
#服务端发送exit,表示让客户端退出
if data.decode() == "EXIT":
sys.exit()
print(data.decode())
#创建网络连接
def main():
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
name = input(">>")
msg = "L " + name#定义请求类别,L为进入聊天室
sockfd.sendto(msg.encode(),ADDR)
#等待回应
data,addr = sockfd.recvfrom(4096)
if data.decode() == 'OK':
print("已允许进入聊天室")
break
else:
print(data.decode())
#创建新的进程
pid = os.fork()
if pid < 0:
print("Error")
elif pid == 0:
send_msg(sockfd,name)
else:
recv_msg(sockfd)
if __name__ == '__main__':
main()
心得:
1.主要采用UDP形式,但是是多线程。
2.客户端创建多线程,因为客户端按照之前的学习是先发送再接受,按照这样的顺序,但是,现在可能进入聊天室,但是giant客户端可能还没有发言就有可能接收到其他人发送的消息,所以,这里的收发消息不是按照一定顺序的,它们的程序之间是相互独立的,那么就要使用多进程关系。
(python自己封装的一个包)
1.流程特点
2.接口基本使用
Process()
功能:创建进程对象
参数:
target:绑定要执行的目标函数(必选)
args :元组,用于给target函数位置传参,即函数的参数。(可选)
kwargs:字典,给target函数键值传参,函数的参数。(可选)
name :进程对象名称。(可选)
p.start()
功能:启动进程
注意:
启动进程此时target绑定函数开始执行,该函数作为子进程执行内容,此时进程真正被创建。
p.join([timeout])
功能:阻塞等待回收函数。(可选参数)
p.join(3)3秒后结束。否则知道p退出后进程才会回收。
参数:超时时间。
代码示例:
#multiprocess实例
import multiprocessing as mp
from time import sleep
#作为子进程函数
def fun01():
print("子程序开始执行")
sleep(3)
print("子程序执行完毕")
#创建进程对象
p = mp.Process(target=fun01)
#启动进程
p.start()
sleep(2)
print("父进程执行的功能")
#回收进程
p.join()
代码示例2:
import multiprocessing as mp
import os
from time import sleep
def fun01():
sleep(3)
print("吃饭")
print(os.getppid(),"------",os.getpid())
def fun02():
sleep(2)
print("睡觉")
print(os.getppid(),"------",os.getpid())
def fun03():
sleep(4)
print("打豆豆")
print(os.getppid(),"------",os.getpid())
thing = [fun01,fun02,fun03]
jobs = []
for th in thing:
p = mp.Process(target=th)
jobs.append(p)#p由于会被重复赋值,那么可以用列表保存进程对象
p.start()
for i in jobs:
i.join()
代码示例3:
#multiprocess示例3
import multiprocessing as mp
import os
from time import sleep
"""
带参数的进程函数
"""
def worker(m,name):
"""
:param m: 秒数
:param name: 姓名
:return:
"""
for i in range(3):
sleep(m)
print("I am %s"%name)
print("开始工作了")
p = mp.Process(target=worker,args=(2,"Jack"))#位置传参
#p = mp.Process(target=worker,kwargs = {'name':"Jack",'s':2})#关键字传参
#p = mp.Process(target=worker,args=(2,),kwargs = {'name':"Jack"})#位置传参和关键字传参混合使用
p.start()
p.join()
在win下运行出现RuntimeError错误,但是在Linux下正常运行,可能和fork有关,这里需要深究下。
注意:
3.进程对象属性
p.name 集成名称
p.id 对应子进程的pid号
p.is_alive() 查看子进程是否在声明周期,在生命周期则返回True,否则返回False。
p.daemon 设置父子进程的退出关系(守护进程)
1.必要性
2.原理
创建一定数量的进程来处理事件,事件处理完进程不退出而是继续处理其他事件,知道所有事件全都处理完毕统一销毁。增加进程的重复利用,降低资源消耗。
3.进程池实现
from multiprocess import pool
Pool(processes)
功能:创建进程池对象
参数:指定进程数量,默认根据系统自动判定。(可选)
pool.apply_async(func,args,kwds)
功能:使用进程池执行func事件
参数:func 事件函数
args 元组,给func按位置传参
kwds 字典,给func按照键值传参
返回值:返回函数事件对象
pool.close()
功能:关闭进程池
pool.join()
功能:回收进程池中进程
代码示例:
from multiprocessing import pool
from time import sleep,ctime
"""
进程池代码示例
"""
#进程池事件
def worker(msg):
sleep(2)
print(msg)
#创建进程池
pool = pool.Pool()
#进程池中添加事件
for i in range(10):
msg = "Hello %d"%i
pool.apply_async(func=worker,args= (msg,))
#关闭进程池
pool.close()
#回收进程池
pool.join()
1.必要性
进程间空间独立,资源不共享,此时需要在进程间数据传输时就需要特定的手段进行数据通信。
2.常用的进程间通信方法
管道
消息队列
共享内存
信号
信号量
套接字
1.通信原理
在内存中开辟管道空间,生成管道操作对象,多个进程使用同一个管道对象进行读写即可实现管道通信。
2.实现方法
当问单向管道时,fd1只读,fd2只写
from multiprocess import Pipe
fd1,fd2 = Pipe(duplex = True)
功能:创建管道
参数:默认表示双向管道
如果duplex为False,则表示单向管道
返回值:表示管道两端的读写对象
如果是双向管道均可读写
如果是单向管道,##fd1只读,fd2只写##。
fd.recv()
功能:从管道获取内容
返回值:获取到的数据
fd.send(data)
功能:向管道写入数据
参数:要写入的数据,非字节串,只要是数据即可
代码示例:(以下示例只能在Linux或者Unix下运行)
#管道通信示例
import multiprocessing as mp
from multiprocessing import Pipe
import time
import os
#创建管道
fd1,fd2 = Pipe(duplex=True)
def fun01(name):
time.sleep(3)
#向管道写入数据
fd1.send({name:os.getpid()})
jobs = []
for i in range(5):
p = mp.Process(target = fun01,args= (i,))
jobs.append(p)
p.start()
for i in range(5):
#读取管道
data = fd2.recv()
print(data)
for i in jobs:
i.join()
1.通信原理(自己写的模块名称不要叫做queue)
在内存中建立队列模型,进程通过队列将消息存入,或者从队列取出完成进程间通信。
队列:先进先出
使用场景:多个进程对一个进程发送请求时,或者一个进程被多个进程使用时。
2.实现方法
from multiprocess import Queue
q = Queue(maxsize = 0)
功能:创建队列对象
参数:最多存放消息个数,默认根据系统给定。
返回值:队列消息
q.put(data,[block,timeout])
功能:向队列存入消息
参数:data 要存入的内容
[block,timeout]
block:设置是否阻塞,False 为非阻塞
阻塞的情况:如果队列满了
timeout:超时检测
凡是有timeout的,基本上都是则色函数。
q.get([block,timeout])
功能:从队列取出消息
参数:block:设置是否阻塞,False 为非阻塞
timeout:超时检测
返回值:返回获取到的内容
q.full() #判断队列是否为满
q.empty() #判断队列是否为空
q.qsize() #获取队列中消息个数
q.close() #关闭队列
代码示例:
#消息队列通信
import multiprocessing as mp
from time import sleep
from random import randint
#创建消息队列
q = mp.Queue(3)#消息队列大小
def request():
for i in range(10):
x = randint(0,100)
y = randint(0,100)
q.put((x,y))
def handle():
while True:
sleep(0.5)
try:
x,y = q.get(timeout=3)
except:
break
else:
print("%d + %d = %d"%(x,y,x + y))
#相当于两个子进程间的通信
p1 = mp.Process(target=request)
p2 = mp.Process(target=handle)
p1.start()
p2.start()
p1.join()
p2.join()
1.通信原理
字啊内存中开辟一块空间,进程可以写入内容或读取内容完成通信,但每次写入内容会覆盖之前内容。
2.实现方法
from multiprocessing import Value,Array
obj = Value(ctype,data)
功能:开辟共享内存
参数:ctype:表示共享内存空间类型 'i' 整型,'f' 浮点型,'c' char型
data:共享内存空间初始数据
返回值:共享内存对象
如:obj = Value('i',10)
obj.value 对象属性的修改查看,即对共享内存读写
obj = Array(ctype,data)
功能:开辟共享内存
参数:ctype:表示共享内存数据类型
data:整数则表示开辟空间的大小,其他数据类型
返回值:共享内存对象
Array 共享内存读写,通过遍历obj可以得到每一个值,直接可以通过索引
*可以直接使用obj.value直接打印共享内存中的字符串。
代码示例 (这个是存放单一数值,存放字符串的同理):
#共享内存
import multiprocessing as mp
import time
import random
#创建共享内存
money = mp.Value('i',10)
#操作共享内存
def man():
for i in range(5):
time.sleep(0.5)
money.value += random.randint(0,100)
def girl():
for i in range(30):
time.sleep(1)
money.value -= random.randint(5,90)
m = mp.Process(target=man)
g = mp.Process(target=girl)
m.start()
g.start()
m.join()
g.join()
#获取共享内存
print("一个月余额:",money.value)
1.功能:
用于本地两个程序之间进行数据的收发,交互时王权不适用网络,而是用本地的套接字文件进行通信。
2.套接字文件
用于本地套接字之间进行通信时,进行数据传输的介质。
扩展内容:
在Linux文件中,cookie:
b c d - l s p
3.创建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM) #只能在Linux或者Unix中运行
sockfd,bind(file)
listen() ->accpet() ->recv(),send()
代码示例:
#接收端作为服务端
from socket import *
import os
#确定本地套接字文件
sock_file = './sock'#不需要手动创建,程序运行会自动创建
#判断文件是否存在,存在就删除
if os.path.exists(sock_file):
os.remove(sock_file)
#创建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
#绑定本地套接字
sockfd.bind(sock_file)
#监听,连接
sockfd.listen(3)
while True:
c,addr = sockfd.accept()
while True:
data = c.recv(1024)
if not data:
break
print(data.decode())
c.close()
sockfd.close()
#本地套接字
from socket import *
#确保两遍使用相同的套接字文件
sock_file = './sock'
sockfd = socket(AF_UNIX,SOCK_STREAM)
socket.connect(sock_file)
while True:
msg = input(">>")
if not msg:
break
sockfd.send(msg.encode())
sockfd.close()
1.通信原理 (08.多任务编程P3-2 03:27)
给定一个数量对多个进程可见,多个进程都可以操作该数量增减,并更具数量值决定自己的行为。
2.实现方法 p3-2 03:40
from multiprocessing import Semaphore
sem = Semaphore(num)
功能:创建信号量对象
参数:信号量的初始值
返回值:信号量对象
sem.acquire() #将信号量减1,当信号量为0是阻塞
sem.release() #将信号量加1
sem.get_value() #获取信号量数量
代码示例:
#信号量
from multiprocessing import Semaphore,Process
from time import sleep
import os
#创建信号量,服务程序最多允许3个进程同时执行事件
sem = Semaphore(3)
def handle():
print("%d 想执行事件"%os.getpid())
#想执行必须获取信号量
sem.acquire()
print("%d 开始执行操作!"%os.getpid())
sleep(3)
print("%d 完成操作"%os.getpid())
sem.release()#增加信号量
jobs = []
#10个进程请求执行事件
for i in range(10):
p = Process(target=handle)
jobs.append(p)
p.start()
for i in jobs:
i.join()
作业:
1.使用multiprocess创建两个进程,同时复制一个文件中的上下两部分,各自复制到一个新的文件中。
00:46
import multiprocessing as mp
import os
class Copy_file:
def __init__(self,file_path):
self.file_path = file_path
self.size = os.path.getsize(self.file_path)
def top(self,save_path):
"""
:param save_path:
:return:
"""
f = open(self.file_path,'rb')
n = self.size // 2
fw = open(save_path,'wb')
fw.write(f.read(n))
f.close()
fw.close()
def bot(self,save_path):
"""
:param save_path:
:return:
"""
f = open(self.file_path,'rb')
fw = open(save_path,'wb')
f.seek(self.size // 2,0)
while True:
data = f.read(1024)
if not data:
break
fw.write(data)
f.close()
fw.close()
def main(self):
p_top = mp.Process(target=self.top,args = ("top_123.jpg",))
p_bot = mp.Process(target=self.bot,args = ("bot_123.jpg",))
p_bot.start()
p_top.start()
p_top.join()
p_bot.join()
if __name__ == '__main__':
cf = Copy_file("123.jpg")
cf.main()
注意: 在上述代码中,在top和bot函数中,如果提取f = open(self.file_path,'rb'),使得的打开操作写在进程之外,会有问题,因为写在进程之外,top和bot操作对于file文件的打开会共用同一套文件属性,如偏移量等信息,使得两个进程在分开进行的时候共用一套文件属性复制出来的数据可能有问题。 如果父进程中打开文件,创建进程通信对象,或者创建套接字,而子进程从父进程内存空间获取这些内容,那么父子进程对该对象的操作会有一定的属性关联影响。
1.什么是线程
2.线程特征
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bfXcxcfW-1591682720464)(./img/线程与进程.png)]
from threading import Thread
t = Thread()
功能:创建线程对象
参数:target 绑定线程函数
args 元组 给线程函数位置传参
kwargs 字典 给线程函数键值传参
name 线程名称
t.start()
t.join([timeout])
代码示例:
"""
线程示例(主线程和分支线程同时执行)
"""
from threading import Thread
from time import sleep
#线程函数
def music():
for i in range(4):
sleep(3)
print("播放歌曲")
#创建线程对象
t = Thread(target= music)
t.start()
#主线程任务
for i in range(3):
sleep(2)
print("播放电影")
t.join()
详解:
daemon 为true时主线程退出分支线程也会退出,要么在start前设置,通常不和join一起使用。
其他:python线程池第三方模块:threadpool
1.创建步骤:
2.使用方法
注意
关于线程函数的高内聚,有可能需要一个线程配合执行多个函数,而t - Thread(target = ?)中只有一个方法参数,如果实现功能实现特别复杂,那么就需要自定义线程类。
代码示例:
from threading import Thread
class ThreadClass(Thread):
def __init__(self,attr):
self.attr = attr
super().__init__()#采用父类的init方法
#多个方法配合实现具体功能
def fun01(self):
print("步骤一")
def fun02(self):
print("步骤二")
#重写run方法
def run(self):
self.fun01()
self.fun02()
t = ThreadClass("xxxxx")
t.start()
t.join()
代码示例2:
from threading import Thread
from time import sleep,ctime
class MyThread(Thread):
def __init__(self,target = None,args= (),
kwargs= {},name = "Tedu"):
super().__init__()
self.target = target
self.args = args
self.kwargs = kwargs
self.name = name
def run(self):
self.target(*self.args,**self.kwargs)
###############################
"""
通过完成上方MyThread类,让整个程序可以正常运行
当调用start时,player作为一个线程功能函数运行
注意,函数的名称和参数并不确定,player只是测试函数
"""
def player(sec,song):
for i in range(2):
print("Playing %s:%s"%(song,ctime()))
sleep(sec)
t = MyThread(target = player,args=(3,),
kwargs={'song':'凉凉'},name = 'happy')
t.start()
t.join()
1.通信方法
线程间使用全局变量进行通信。
2.共享资源争夺
3.同步互斥机制
同步:同步是一种协调关系,为完成操作,多进程或线程间形成一种协调,按照必要的步骤有序执行操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oyuPxFRA-1591682720468)(./img/同步原理.png)]
互斥:互斥是一种制约机制,当一个进程或线程占有资源时会进行枷锁处理,此时其它进程线程无法操作此资源,直到解锁后才能操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H1wolPA2-1591682720469)(./img/互斥原理.png)]
from threading import Event
e = Event() #创建线程事件对象
e.wait([timeout]) #阻塞等待e被set,timeout超时时间
e.set() #设置e,使得wait结束阻塞
e.clear() #使e回到未被设置状态
e.is_set() #查看当前e是否被设置
代码示例:
#event事件代码示例
from threading import Event
from threading import Thread
from time import sleep
s = None #全局变量,模拟对暗号
e = Event()
def 杨子荣():
print("杨子荣前来拜上头")
global s
s = "天王盖地虎"
e.set() #共享资源操作完毕
t = Thread(target=杨子荣)
t.start()
print("说对口令就是自己人")
#每次验证之前进行阻塞等待
e.wait()
if s == "天王盖地虎":
print("宝塔镇河妖")
print("口令正确")
else:
print("枪毙")
t.join()
from threading import lock
lock = Lock() #创建锁对象
lock.acquire() #上锁 如果lock已经上锁,再调用会阻塞
lock.release() #解锁
with lock: #上锁
···
···
wait代码块结束后自动解锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lr6HgnVj-1591682720470)(./img/同步互斥原理LOCK.png)]
代码示例:
#同步互斥原理LOCK代码示例
from threading import Lock,Thread
a = b = 0 #全部变量,共享资源
lock = Lock()
def value():
while True:
lock.acquire()
if a != b:
print("a = %d,b = %d"%(a,b))
lock.release()
t = Thread(target=value)
t.start()
while True:
with lock:
a += 1
b += 1
t.join()
定义
死锁实质两个或两个以上的线程在执行过程中,由于竞争资源或者彼此通信二造成的一种阻塞现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或者系统产生了死锁。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t4WmHBjE-1591682720472)(./img/死锁现象.png)]
死锁发生的必要条件:
死锁产生的原因:
- 当线程拥有其他线程需要的资源。
- 当前线程等待其它线程已拥有的资源。
- 都不放弃自己拥有的资源。
阻塞代码示例:
import threading
import time
class Account:
"""
交易类
"""
def __init__(self,_id,balance,lock):
"""
:param _id: 用户
:param balance: 存款
:param lock: 锁
"""
self.id = _id
self.balance = balance
self.lock = lock
def withdraw(self,amount):
"""
:param amount:
:return:
"""
self.balance -= amount
def deposit(self,amount):
"""
:param amount:
:return:
"""
self.balance += amount
#查看账户金额
def get_balance(self):
return self.balance
#转账函数
def transfer(from_,to,amount):
if from_.lock.acquire():# 锁着自己的账户,上锁成返回true
from_.withdraw(amount) # 自己账户金额减少
if to.lock.acquire():
to.deposit(amount) # 对方账户金额增加
to.lock.release()# 对方账户解锁
from_.lock.release()# 自身账户解锁
print("转账完成")
#创建两个账户
Abby = Account('Abby',5000,threading.Lock())
Levi = Account('Levi',3000,threading.Lock())
t1 = threading.Thread(target=transfer,args=(Abby,Levi,1500))
t2 = threading.Thread(target=transfer,args=(Levi,Abby,1500))
t1.start()
t2.start()
t1.join()
t1.join()
print("Abby:",Abby.get_balance())
print("Levi:",Levi.get_balance())
未完待续…