from multiprocessing import JoinableQueue # join 是等待某个任务运行完毕 able是可以的意思 Queue是队列 综合起来 就是一个可以被join的队列 继承自Queue j = JoinableQueue() # j.put('123') # 放入信息 # print(j.get()) # 获取信息 # j.task_done() # 告知队列这次数据取完了 不是表示任务全部处理完成 而是 取出来某个数据处理完成 j.put('wsx') j.put('222') j.put('333') print('取走了%s'% j.get()) j.task_done() j.task_done() # j.task_done() j.join() # 只有取完了 才可以继续进行 没取完则一直等 # :结论 task_done() 和.put()次数要对应起来 即放入几次数据要有几次告知信息
from multiprocessing import Process,JoinableQueue import time,random def make_hot_dog(name,q): for i in range(5): time.sleep(random.randint(1,3)) print('%s正在生产%s个热狗'%(name,i) ) q.put('%s的%s热狗'%(name,i)) # 放入容器 def eat_hot_dog(name,q): while True: hot_dog= q.get() # 取出容器内容 time.sleep(random.randint(1, 3)) print('%s吃掉了%s'% (name,hot_dog)) q.task_done() # 告知取出了数据 if __name__ == '__main__': q=JoinableQueue() # 建立队列 p1 = Process(target=make_hot_dog,args=('owen的热狗店',q)) p2 = Process(target=make_hot_dog,args=('jerry的热狗店',q)) p3 = Process(target=make_hot_dog,args=('take的热狗店',q)) c1 = Process(target=eat_hot_dog,args=('思聪',q)) c1.daemon = True # 守护进程 c2 = Process(target=eat_hot_dog,args=('蔡徐坤',q)) c2.daemon = True p1.start() p2.start() p3.start() c1.start() c2.start() p1.join() # 保证子进程先于主线程运行 print('第一家生产完毕') p2.join() print('第二家生产完毕') p3.join() print('第san家生产完毕') c1.join() print('热狗吃完了') c2.join() print('热狗吃完了')
2 线程
线程是操作系统最小运算调度单位 ,线程被包裹在进程当中 ,线程指的就是固定的执行流程
2.2 线程和进程的关系
1 线程: 是一个正在运行的程序, 是操作系统将数据加载到内存中去运行,那么进程就产生了,那么可以理解进程是是一个资源单位,其包含运行程序所需要的所有资源.
2 进程:是操作系统最小运算调度单位,是被包含在进程当中的 ,任务都是由线程执行的,
3:线程是依附进程存活的,没有线程,进程的资源是无法被调动执行的 ,所以一个进程至少包含一个线程,那这个线程也称之为主线程.(主线程是由操作系统启动一个程序时,就自动会为其创建一个主线程)
4:线程也可以由程序后期取开启 ,这时开启的线程叫做子线程
2.3 为毛需要多线程
目的:为了提高效率 (多线程处理数据比单个线程处理速度要快的多)
3 如何使用线程
主要语法和进程一样
区别在进程开启子进程方法必须在main下面. 线程可以任意位置开启 因为内存中的进程时相互隔离的 必须加以判断 而线程是在进程当中的 数据可以共享的
3.1线程两种创建方式
## 2 线程 ### 2.1什么是线程: 线程是操作系统最小运算调度单位 ,线程被包裹在进程当中 ,线程指的就是固定的执行流程 ### 2.2 线程和进程的关系 1 线程: 是一个正在运行的程序, 是操作系统将数据加载到内存中去运行,那么进程就产生了,那么可以理解进程是是一个资源单位,其包含运行程序所需要的所有资源. 2 进程:是操作系统最小运算调度单位,是被包含在进程当中的 ,任务都是由线程执行的, 3:线程是依附进程存活的,没有线程,进程的资源是无法被调动执行的 ,所以一个进程至少包含一个线程,那这个线程也称之为主线程.(主线程是由操作系统启动一个程序时,就自动会为其创建一个主线程) 4:线程也可以由程序后期取开启 ,这时开启的线程叫做子线程 ### 2.3 为毛需要多线程 目的:为了提高效率 (多线程处理数据比单个线程处理速度要快的多) ## 3 如何使用线程 主要语法和进程一样 区别在进程开启子进程方法必须在main下面. 线程可以任意位置开启 因为内存中的进程时相互隔离的 必须加以判断 而线程是在进程当中的 数据可以共享的 ### 3.1线程两种创建方式
3.2 线程的特点
1:线程开启的资源占用小 (与开进程做对比)
2:同一程序中 线程之间数据是共享的
3: 线程之间没有父子之分,等级是平等的 所有线程pip都是一样的
验证案例
1:线程开启的资源占用小 (与开进程做对比) 开启100进程和100个线程 看运算时间 做对比 线程所需时间远远小于开进程 from multiprocessing import Process from threading import Thread import time def task(): pass if __name__ == '__main__': t_time = time.time() for i in range(100): # p=Process(target=task) p = Thread(target=task) p.start() print(time.time()-t_time)
# 2:同一程序中 线程之间数据是共享的 from threading import Thread a = 10 def task(): global a a=20 print('子线程running') print(a) t=Thread(target=task) t.start() print('主线程running') print(a) # 子线程runing # 20 # 主线程runing # 20
3: 线程之间没有父子之分,等级是平等的 所有线程pip都是一样的
import os from threading import Thread def task(): pass for i in range(5): t= Thread(target=task) t.start() print(os.getpid()) # 打印的结果都一样
4 守护线程 了解 t.daemon = Ture
一个线程可以守护另外一个线程 当被守护线程结束 自己也随之结束
import random import time from threading import Thread def task(): print('子线程running!') time.sleep(5) print('子线程over!') if __name__ == '__main__': t= Thread(target=task) t.daemon = True # 设置守护进程 现在子线程守护主线程 当主线程挂了 子线程就不在运行了 所以主线程over之后子线程('子线程over!')这句就不走了 t.start() print('主线程running!' ) time.sleep(random.randint(1,3)) print('主线程over!') # 运行结果 #子线程running! # 主线程running! # 主线程over! # 注意点 此题中如果还有其他子线程 且没有进行守护 那么主线程要等到那个子线程结束才能结束 此时守护的那个子线程再算结束
5 线程安全问题 ==>线程互斥锁
因为线程之间数据是共享的 数据共享意味着可以多个线程操作同一个资源 那么就有可能导致数据的一个错乱 那么这时就导入线程互斥锁的 和进程的进程互斥锁类似
# 通过10次子线程将10 变成0 那么就有多个子线程操作同一个资源 防止错乱 就得加上锁 那么主线程也得先让子线程运行完采取执行 需要join import time from threading import Thread,Lock,enumerate look= Lock() # 建锁 num = 10 def task(): global num look.acquire() a = num time.sleep(0.1) num = a-1 look.release() for i in range(10): t = Thread(target=task) t.start() # print(enumerate()) 会显示所有线程名称 for t in enumerate()[1:]: # 返回当前活动的所有Thread对象的列表。 t.join() print(num)
""" 死锁问题 当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源要想使用必须同时具备两把锁 这时候程序就会进程无限卡死状态 ,这就称之为死锁 例如: 要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子 如何避免死锁问题 锁不要有多个,一个足够 如果真的发生了死锁问题,必须迫使一方先交出锁 """ import time from threading import Thread,Lock,current_thread # 盘子 lock1 = Lock() # 筷子 lock2 = Lock() def eat1(): lock1.acquire() print('%s抢到了盘子' % current_thread().name) lock1.acquire() time.sleep(0.5) lock2.acquire() print('%s抢到了筷子' % current_thread().name) lock2.release() time.sleep(0.5) print('%s开吃了' % current_thread().name) time.sleep(0.5) print('%s放下了筷子' % current_thread().name) time.sleep(0.5) print('%s放下了盘子' % current_thread().name) def eat2(): lock2.acquire() print('%s抢到了筷子' % current_thread().name) lock2.release() time.sleep(0.5) lock1.acquire() print('%s抢到了盘子' % current_thread().name) lock1.release() time.sleep(0.5) print('%s开吃了' % current_thread().name) time.sleep(0.5) print('%s放下了盘子' % current_thread().name) time.sleep(0.5) print('%s放下了筷子' % current_thread().name) t1 = Thread(target=eat1) t2 = Thread(target=eat2) t1.start() t2.start() # 这就出现了死锁问题 # Thread-1抢到了盘子 # Thread-2抢到了筷子 之后等着进行 没有退出
""" Rlock 了解 称之为递归锁 或者可重入锁 Rlock不使用用来解决死锁问题的 与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程已经执行过acquire 其他线程将无法执行acquire """ from threading import RLock, Lock, Thread # l = Lock() # # l.acquire() # print("1") # l.acquire() # print("2") l = RLock() # l.acquire() # print("1") # l.acquire() # print("2") def task(): l.acquire() print("子run......") l.release() # 主线程锁了一次 l.acquire() l.acquire() l.release() l.release() t1 = Thread(target=task) t1.start()
""" 信号量 了解 可以现在被锁定的代码 同时可以被多少线程并发访问 Lock 锁住一个马桶 同时只能有一个 Semaphore 锁住一个公共厕所 同时可以来一堆人 用途: 仅用于控制并发访问 并不能防止并发修改造成的问题 """ from threading import Semaphore, Thread import time s = Semaphore(5) def task(): s.acquire() print("子run") time.sleep(3) print("子over") s.release() for i in range(10): t = Thread(target=task) t.start()