day34:线程&守护线程&线程锁&事件
目录
线程的基本使用
首先,明确一下线程和进程的定义
进程:资源分配的最小单位
线程:cpu执行程序的最小单位
1.一个进程资源中可以包含多个线程
def func(num): time.sleep(random.uniform(0.1,1)) print("当前进程{},参数是{}".format(os.getpid(),num)) for i in range(10): # 在一个进程资源中创建了十个线程 t = Thread(target=func,args=(i,)) t.start() print(os.getpid())
运行结果如下图所示
2.并发的多线程和多进程谁的速度快? 多线程!
计算多线程执行时间
def func(num): print("当前进程{},参数是{}".format(os.getpid(), num)) if __name__ == "__main__": # 多线程 lst = [] # 记录开始时间 startime = time.time() 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("多线程执行时间",(endtime - startime)) # 0.27
如图所示,跑1000个线程只需要0.27s
计算多进程执行时间
def func(num): print("当前进程{},参数是{}".format(os.getpid(), num)) if __name__ == "__main__": lst = [] startime = time.time() for i in range(1000): t = Process(target=func, args=(i,)) t.start() lst.append(t) # 等到所有的子线程执行完毕 for i in lst: i.join() # 计算结束时间 endtime = time.time() print("多进程执行时间", (endtime - startime)) # 63.30
如图所示,跑1000个进程需要63.30s
所以,我们得出结论!并发的多线程要远比多进程快!!!
3.多线程之间,共享同一份进程资源
num = 1000 def func(): global num num -= 1 for i in range(1000): t = Thread(target=func) t.start() print(num)
运行结果如下图所示
用类定义线程
class MyThread(Thread): def __init__(self,name): # 手动调用父类的构造方法 super().__init__() self.name = name def run(self): time.sleep(1) print("当前进程号码是{},名字是{}".format(os.getpid() , self.name)) if __name__ == "__main__": t = MyThread("当前是一个线程") t.start() print("主线程执行结束 ... ")
线程相关的函数
1.线程.is_alive() 检测线程是否仍然存在
2.线程.setName() 设置线程名字
3.线程.getName() 获取线程名字
def func(): time.sleep(1) if __name__ == "__main__": t = Thread(target=func) t.start() # 检测线程是否仍然存在 print(t.is_alive()) # True # 获取线程名字 print(t.getName()) # Thread-1 # 设置线程名字 t.setName("xboyww") # 获取线程名字 print(t.getName()) # xboyww
1.currentThread().ident 查看线程id号
def func(): print("子线程的线程id{}".format(currentThread().ident)) if __name__ == "__main__": Thread(target=func).start() print("主线程的线程id{}".format(currentThread().ident))
运行结果如下图所示
2.enumerate() 返回目前正在运行的线程列表
3.activeCount() 返回目前正在运行的线程数量
def func(): print("子线程的线程id{}".format(currentThread().ident)) time.sleep(0.5) if __name__ == "__main__": for i in range(10): Thread(target=func).start() lst = enumerate() # 主线程 + 10个子线程 print(lst, len(lst)) # 3.activeCount() 返回目前正在运行的线程数量 print(activeCount()) # 11
运行结果如下图所示
守护线程
守护线程和守护进程不同
守护进程是守护主进程,主进程结束,守护进程立刻被杀死
守护线程是守护所有线程,必须等所有线程结束,守护线程才会被杀死
def func1(): while True: time.sleep(0.5) print("我是func1") def func2(): print("我是func2 start ... ") time.sleep(3) print("我是func2 end ... ") def func3(): print("我是func3 start ... ") time.sleep(5) print("我是func3 end ... ") if __name__ == "__main__": t1 = Thread(target=func1) t2 = Thread(target=func2) t3 = Thread(target=func3) # 在start调用之前,设置线程为守护线程 t1.setDaemon(True) t1.start() t2.start() t3.start() print("主线程执行结束 .... ")
运行结果如下图所示
线程中安全问题:Lock
现在,我们想完成如下的操作:
1.在线程p1中for循环100万次,每次完成一次+1操作
2.在线程p2中for循环100万洗,每次完成一次-1操作
根据想法,我们可以写出如下代码:
n = 0 def func1(lock): global n for i in range(1000000): n += 1 # lock.release() def func2(lock): global n for i in range(1000000): n -= 1 if __name__ == "__main__": lst = [] lock = Lock() startime = 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() endtime = time.time() print("主线程执行结束 ... 打印{} 时间是{}".format(n, endtime - startime))
执行结果如下图所示
然而,这并不是我们想要的结果
所以我们需要在线程t1和线程t2加锁
运行代码,发现得到的结果正是我们想要的结果
信号量:Semaphore
线程的Semaphore和进程的Semaphore完全一致,在此就不过多赘述了
def func(i,sm): # 上锁 + 解锁 with sm: print(i) time.sleep(3) if __name__ == "__main__": # 支持同一时间,5个线程上锁 sm = Semaphore(5) for i in range(20): Thread(target=func,args=(i,sm)).start() """ 再创建线程的时候是异步创建 在执行任务时,遇到Semaphore进行上锁,会变成同步程序 """
死锁 互斥锁 递归锁
1.语法上的死锁
只上锁不解锁,一定会产生死锁
lock = Lock() lock.acquire() lock.acquire() lock.acquire() # 只上锁不解锁,会产生死锁。运行程序会发生阻塞 lock.release() print(1)
2.逻辑上的死锁
noodle_lock = Lock() kuaizi_lock = Lock() def eat1(name): noodle_lock.acquire() print("%s 抢到面条了" % (name)) kuaizi_lock.acquire() print("%s 抢到筷子了" % (name)) print("开始享受面条 ... ") time.sleep(0.5) kuaizi_lock.release() print("%s 放下筷子" % (name)) noodle_lock.release() print("%s 放下面条" % (name)) def eat2(name): kuaizi_lock.acquire() print("%s 抢到筷子了" % (name)) noodle_lock.acquire() print("%s 抢到面条了" % (name)) print("开始享受面条 ... ") time.sleep(0.5) noodle_lock.release() print("%s 放下面条" % (name)) kuaizi_lock.release() print("%s 放下筷子" % (name)) if __name__ == "__main__": name_lst1 = ["Fly", "Hurt"] name_lst2 = ["Alan", "Cat"] for name in name_lst1: Thread(target=eat1, args=(name,)).start() for name in name_lst2: Thread(target=eat2, args=(name,)).start()
运行结果如下图所示
3.递归锁
上方的示例造成了逻辑上的死锁现象
想要解决这种情况,我们需要递归锁。
什么是递归锁?
递归锁专门用来解决这种死锁现象
临时用于快速解决线上项目发生阻塞死锁问题的
from threading import RLock rlock = RLock() rlock.acquire() rlock.acquire() rlock.acquire() rlock.acquire() print(112233) rlock.release() rlock.release() rlock.release() rlock.release() print("程序结束 ... ")
运行结果如下图所示
4.用递归锁解决2中(面条-筷子)的死锁现象
noodle_lock = kuaizi_lock = RLock() def eat1(name): noodle_lock.acquire() print("%s 抢到面条了" % (name)) kuaizi_lock.acquire() print("%s 抢到筷子了" % (name)) print("开始享受面条 ... ") time.sleep(0.5) kuaizi_lock.release() print("%s 放下筷子" % (name)) noodle_lock.release() print("%s 放下面条" % (name)) def eat2(name): kuaizi_lock.acquire() print("%s 抢到筷子了" % (name)) noodle_lock.acquire() print("%s 抢到面条了" % (name)) print("开始享受面条 ... ") time.sleep(0.5) noodle_lock.release() print("%s 放下面条" % (name)) kuaizi_lock.release() print("%s 放下筷子" % (name)) if __name__ == "__main__": name_lst1 = ["Fly","Hurt"] name_lst2 = ["Cat","Alan"] for name in name_lst1: Thread(target=eat1,args=(name,)).start() for name in name_lst2: Thread(target=eat2,args=(name,)).start()
运行结果如下图所示
5.用互斥锁解决2中(面条-筷子)的死锁现象
用互斥锁解决问题,换句话来说,就是尽量用一把锁解决问题
mylock = Lock() def eat1(name): mylock.acquire() print("%s 抢到面条了" % (name)) print("%s 抢到筷子了" % (name)) print("开始享受面条 ... ") time.sleep(0.5) print("%s 放下筷子" % (name)) print("%s 放下面条" % (name)) mylock.release() def eat2(name): mylock.acquire() print("%s 抢到筷子了" % (name)) print("%s 抢到面条了" % (name)) print("开始享受面条 ... ") time.sleep(0.5) print("%s 放下面条" % (name)) print("%s 放下筷子" % (name)) mylock.release() if __name__ == "__main__": name_lst1 = ["A","B"] name_lst2 = ["C","D"] for name in name_lst1: Thread(target=eat1,args=(name,)).start() for name in name_lst2: Thread(target=eat2,args=(name,)).start()
事件:Event
e = Event()
wait 动态添加阻塞
clear 将内部的阻塞值改成False
set 将内部的阻塞值改成True
is_set 获取内部的阻塞值状态(True False)
基本语法
e = Event() print(e.is_set()) e.set() print(e.is_set()) e.clear() print(e.is_set()) # 代表最多阻塞3秒 e.wait(3) print("程序运行中... ")
模拟链接远程数据库
def check(e): # 用一些延迟来模拟检测的过程 time.sleep(random.randrange(1,6)) # 1 2 3 4 5 # time.sleep(1) print("开始检测链接用户的合法性") e.set() def connect(e): sign = False for i in range(1,4): # 1 2 3 # 设置最大等待1秒 e.wait(1) if e.is_set(): print("数据库链接成功 ... ") sign = True break else: print("尝试链接数据库第%s次失败 ... " % (i)) if sign == False: # 主动抛出异常,(超时异常) raise TimeoutError e = Event() # 线程1号负责执行连接任务 Thread(target=connect,args=(e,)).start() # 线程2号负责执行检测任务 Thread(target=check,args=(e,)).start()
运行结果如下图所示