day34:线程&守护线程&线程锁&事件
目录1.线程的基本使用 2.用类定义线程 3.线程相关的函数 4.守护线程 5.线程中安全问题:Lock 6.信号量:Semaphore 7.死锁 互斥锁 递归锁 8.事件:Event 线程的基本使用首先,明确一下线程和进程的定义 进程:资源分配的最小单位 线程: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.并发的多线程和多进程谁的速度快? 多线程!计算多线程执行时间 func(num): if __name__ == __main__: 多线程 lst = [] 记录开始时间 startime = time.time() in range(1000): t = Thread(target=func,)) t.start() lst.append(t) 等到所有的子线程执行完毕 in lst: i.join() 计算结束时间 endtime =多线程执行时间",(endtime - startime)) 0.27 如图所示,跑1000个线程只需要0.27s 计算多进程执行时间 : lst = [] startime =): t = Process(target=func,1)">多进程执行时间 63.30 如图所示,跑1000个进程需要63.30s 所以,我们得出结论!并发的多线程要远比多进程快!!! 3.多线程之间,共享同一份进程资源num = 1000 func(): global num num -= 1 ): t = Thread(target=func) t.start() print(num) 运行结果如下图所示 用类定义线程class MyThread(Thread): def __init__(self,name): 手动调用父类的构造方法 super().() self.name = name run(self): time.sleep(1) 当前进程号码是{},名字是{}: t = MyThread(当前是一个线程) t.start() 主线程执行结束 ... ") 线程相关的函数1.线程.is_alive() 检测线程是否仍然存在 2.线程.setName() 设置线程名字 3.线程.getName() 获取线程名字 func(): time.sleep(1) : 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号 子线程的线程id{}.format(currentThread().ident)) : Thread(target=func).start() 主线程的线程id{}".format(currentThread().ident)) 运行结果如下图所示 2.enumerate() 返回目前正在运行的线程列表 3.activeCount() 返回目前正在运行的线程数量 .format(currentThread().ident)) time.sleep(0.5) in range(10): Thread(target=func).start() lst = enumerate() 主线程 + 10个子线程 (lst,len(lst)) 3.activeCount() 返回目前正在运行的线程数量 print(activeCount()) 11 运行结果如下图所示 守护线程守护线程和守护进程不同 守护进程是守护主进程,主进程结束,守护进程立刻被杀死 守护线程是守护所有线程,必须等所有线程结束,守护线程才会被杀死 func1(): while True: time.sleep(0.5我是func1 func2(): 我是func2 start ... ) time.sleep(3我是func2 end ... func3(): 我是func3 start ... ) time.sleep(5我是func3 end ... : t1 = Thread(target=func1) t2 = Thread(target=func2) t3 = Thread(target=func3) 在start调用之前,设置线程为守护线程 t1.setDaemon(True) t1.start() t2.start() t3.start() 主线程执行结束 .... ") 运行结果如下图所示 线程中安全问题:Lock现在,我们想完成如下的操作: 1.在线程p1中for循环100万次,每次完成一次+1操作 2.在线程p2中for循环100万洗,每次完成一次-1操作 根据想法,我们可以写出如下代码: n = 0 func1(lock): n in range(1000000): n += 1 lock.release() func2(lock): ): n -= 1 [] lock = Lock() startime =): t1 = Thread(target=func1,1)">(lock,)) t2 = Thread(target=func2,)) t1.start() t2.start() lst.append(t1) lst.append(t2) lst: i.join() endtime =主线程执行结束 ... 打印{} 时间是{}".format(n,endtime - startime)) 执行结果如下图所示 然而,这并不是我们想要的结果 所以我们需要在线程t1和线程t2加锁 运行代码,发现得到的结果正是我们想要的结果 信号量:Semaphore线程的Semaphore和进程的Semaphore完全一致,在此就不过多赘述了 func(i,sm): 上锁 + 解锁 with sm: (i) time.sleep(3) 支持同一时间,5个线程上锁 sm = Semaphore(5in range(20): Thread(target=func,sm)).start() """ 再创建线程的时候是异步创建 在执行任务时,遇到Semaphore进行上锁,会变成同步程序 """ 死锁 互斥锁 递归锁1.语法上的死锁只上锁不解锁,一定会产生死锁 lock = Lock() lock.acquire() lock.acquire() lock.acquire() 只上锁不解锁,会产生死锁。运行程序会发生阻塞 lock.release() print(1) 2.逻辑上的死锁noodle_lock = Lock() kuaizi_lock = Lock() eat1(name): noodle_lock.acquire() %s 抢到面条了" % (name)) kuaizi_lock.acquire() %s 抢到筷子了 (name)) 开始享受面条 ... ) time.sleep(0.5) kuaizi_lock.release() %s 放下筷子 (name)) noodle_lock.release() %s 放下面条 (name)) eat2(name): kuaizi_lock.acquire() (name)) noodle_lock.acquire() ) noodle_lock.release() (name)) kuaizi_lock.release() : name_lst1 = [FlyHurt] name_lst2 = [AlanCat] for name name_lst1: Thread(target=eat1,1)">(name,)).start() 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() 程序结束 ... ") 运行结果如下图所示 4.用递归锁解决2中(面条-筷子)的死锁现象noodle_lock = kuaizi_lock = RLock() (name)) |