1.并发和并行
并发:在逻辑上具备同时处理多个任务的能力(其实每时刻只有一个任务)
并行:物理上在同一时刻执行多个并发任务
2.线程与进程
一个进程管多个线程,一个进程至少有一个线程
python多线程是假的,因为python解释器内置同一时间CPU只运行一个线程
import threading
import time
def foo(something):
for i in range(10):
print(something)
time.sleep(1)
#创建线程
t1=threading.Thread(target=foo,args=("看电影",))
t2=threading.Thread(target=foo,args=("听音乐",))
#启动线程
t1.start()
t2.start()
3.join函数-阻塞主线程
一个主线程,2个子线程t1和t2
需要t1和t2运行结束之前,数据检查不会运行,也就是把主线程阻塞
import threading
import time
def foo(something):
for i in range(10):
print(something)
time.sleep(1)
#创建线程
t1=threading.Thread(target=foo,args=("数据逻辑1",))
t2=threading.Thread(target=foo,args=("数据逻辑2",))
#启动线程
t1.start()
t2.start()
#join在子线程完成运行之前,这个子线程的父线程将一直被阻塞
t1.join()
t2.join()
print("启动数据检查....")
4.守护线程
消费需求满足后,数据逻辑1和2不需要再运行了,直接退出主线程
如果没有声明守护线程,主线程就要等子线程全部执行完才退出
import threading
import time
def foo(something):
for i in range(10):
print(something)
time.sleep(1)
#创建线程
t1=threading.Thread(target=foo,args=("数据逻辑1",))
t2=threading.Thread(target=foo,args=("数据逻辑2",))
#声明守护线程,必须再start方法之前,声明守护线程
t1.setDaemon(True)
t2.setDaemon(True)
#启动线程
t1.start()
t2.start()
for i in range(10):
print("消费数据....")
time.sleep(1)
print("消费需求已经满足了")
4.不安全的并发
场景:
银行卡余额:500元
发工资1000元,买衣服花费200元,二者同时进行
不安全的情况:
(1)发工资的线程拿到了500,加上1000,实际余额为1500元,存入余额
(2)买衣服的线程拿到了500,花费200,实际余额为300元,存入余额
结果:300覆盖了1500,造成损失
解决:加锁–同步锁
import threading,time
account_balance=500
#创建锁--同步锁
lock1=threading.Lock()
#操作账户余额
def foo(num):
#上锁
lock1.acquire()
#局部可以直接使用全局变量,但修改全局变量需要声明对全局变量的引用
global account_balance
banlance=account_balance#通过接口调用取到账户余额
time.sleep(1)
banlance=banlance+num
#计算结束,将结果存辉数据库或通过接口返回新的余额
account_balance=banlance
#操作数据结束后,解锁
lock1.release()
t1=threading.Thread(target=foo,args=(1000,))#银行发工资
t2=threading.Thread(target=foo,args=(-200,))#花钱
#启动线程
t1.start()
t2.start()
t1.join()
t2.join()
print("最终余额",account_balance)
5.死锁
两个线程都各自占一部分资源,又同时去等待对方的资源,陷入一种僵局
import threading,time
lockA=threading.Lock()#面试官持有的锁
lockB=threading.Lock()#小明的锁
#面试官
def foo1():
lockA.acquire()#上锁
print("请解释什么是死锁")
time.sleep(1)
lockB.acquire()#小明上锁
print("解释完发offer")
time.sleep(1)
#解锁
lockA.release()
lockB.release()
#小明
def foo2():
lockB.acquire()#上锁
print("请给我offer")
time.sleep(1)
lockA.acquire()#上锁
print("发offer再解锁什么是死锁")
time.sleep(1)
lockA.release()
lockB.release()
t1=threading.Thread(target=foo1)
t2=threading.Thread(target=foo2)
t1.start()
t2.start()
6.解决和预防死锁
递归锁(可重入锁),内部有计数器
用了递归锁:可以多次访问同一个资源
import threading,time
rLock=threading.RLock()#递归锁,也叫可重入锁
#面试官
def foo1():
rLock.acquire()#上锁
print("你给我蛋糕")
time.sleep(1)
rLock.acquire()#小明上锁
print("我给你草莓")
time.sleep(1)
#解锁
rLock.release()
rLock.release()
#小明
def foo2():
rLock.acquire()#上锁
print("你给我草莓")
time.sleep(1)
rLock.acquire()#上锁
print("我给你蛋糕")
time.sleep(1)
rLock.release()
rLock.release()
t1=threading.Thread(target=foo1)
t2=threading.Thread(target=foo2)
t1.start()
t2.start()
读的时候用递归锁,写的时候用同步锁