线程不能单独存在 必须存在于进程中,
进程是一个资源单位,其包含了运行程序所需的所有资源
线程才是真正的执行单位
没有线程,进程中的资源无法被利用起来,所以一个进程至少包含一个线程,称之为主线程
当我们启动一个程序时,操作系统就会自己为这个程序创建一个主线程
为什么需要线程
目的只有一个就是提高效率
就像一个车间 如果产量跟不上 就再造一条流水线
当然可以再造一个新车间,那需要把原材料运过去 ,这个过程是非常耗时的
特点:
1.每个进程都会有一个默认的线程
3.同一进程中的所有线程之间数据是共享的
4.创建线程的开销远比创建进程小的多
主线程与子线程的区别:
1.线程之间是没有父子之分,是平等的
2.主线程是由操作系统自动开启的,而子线是由程序主动开启
3.即使主线程的代码执行完毕,也不会结束进程,会等待所有线程执行完毕,进程才结束
JoinableQueue队列 from multiprocessing import JoinableQueue
# 可以被join的队列 q = JoinableQueue() print('------------') q.put('123') q.put('456') print('取走了%s'% q.get()) q.task_done() # 告诉队列这个数据已经被处理完毕 # 而不是表示任务全部处理完成 # 只是取出某个数据处理完成 print('-----------') print('取走了%s'% q.get()) q.task_done() q.join() # 等待队列中的数据被处理完毕 print('over') # task_done=put 调用次数相等 进程才会结束 生产者消费者模型
import random import time from multiprocessing import Process,Queue ,JoinableQueue def make_ice_cream(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_ice_cream(name,q): while True: ice_cream=q.get() # if not ice_cream: # break time.sleep(random.randint(1,3)) print('%s吃掉了%s'%(name,ice_cream)) # 必须记录该数据处理完成了 q.task_done() if __name__ == '__main__': q = JoinableQueue() p1 = Process(target=make_ice_cream,args=('阿三的冰激凌店',q)) p2 = Process(target=make_ice_cream, args=('阿肆的冰激凌店', q)) p3 = Process(target=make_ice_cream, args=('阿五的冰激凌店', q)) c1 = Process(target=eat_ice_cream,args=('大王',q)) c1.daemon=True c2 = Process(target=eat_ice_cream, args=('二王', q)) c2.daemon=True c3 = Process(target=eat_ice_cream, args=('三王', q)) c3.daemon=True p1.start() p2.start() p3.start() c1.start() c2.start() c3.start() # 目前的思路 是当商家做完以后 放一个None 作为结束标志 而且 必须明确商家和消费者的个数 # 明确商家生成完毕 再明确消费者吃完了 就算结束 p1.join() print("第一家生成完毕") p2.join() print("第二家生成完毕") p3.join() print("第三家生成完毕") # 消费者吃完了 q.join() print('消费者吃完了')
创建线程的俩种方式
from threading import Thread, current_thread import time # current_thread:当前线程 def task(): print('1', current_thread()) print('子线程running') time.sleep(5) print('子线程over') # 方法一:直接实例化Thread类 if __name__ == '__main__': t = Thread(target=task) t.start() task() print('主线程over') print('1', current_thread()) # 执行顺序不固定 如果开启线程速度足够快 可能子线程先执行 # 方法2 class MyThread(Thread): def run(self): print('子线程run') m = MyThread() m.start() print('主线over') # 使用方法和多进程一模一样 开启线程的代码可以放在任何位置 开启进程必须放在判断下面
线程与进程区别:
1.同一进程中 线程之间数据共享
a = 100 def task(): global a print("子线程 run........") a = 1 t = Thread(target=task) t.start() print(a) # 1 print("over")
2.创建线程的开销远比创建进程小的多
from threading import Thread from multiprocessing import Process import time def task(): pass if __name__ == '__main__': start = time.time() for i in range(100): p = Thread(target=task) p.start() print(time.time()-start) # 修改Thread 为Process类 查看结果
3.无论开启了多少子线程PID是不会变的
from threading import Thread import os def task(): print(os.getpid()) for i in range(100): p = Thread(target=task) p.start()
Tread类的
# threading模块包含的常用方法
import threadingprint(threading.current_thread().name) #获取当前线程对象print(threading.active_count()) # 获取目前活跃的线程数量print(threading.enumerate()) # 获取所有线程对象 t = Thread(name="aaa")# t.join() # 主线程等待子线程执行完毕print(t.name) # 线程名称print(t.is_alive()) # 是否存活print(t.isDaemon()) # 是否为守护线程
# 主线程代码执行完毕后,不会立即结束,会等待其他子线程结束 # 主线程 会等待非守护线程结束后结束 # 如果守护线程已经完成任务,程序立马结束
ef task(): print('子线程1 跑........') time.sleep(2) print('子线程1 关.....') def task2(): print('子线程2 跑........') time.sleep(2) print('子线程2 关.....') t1=Thread(target=task) t1.daemon=True t1.start() t2=Thread(target=task2) t2.start() print('主线程 关.....') 顺序是:守护线程 等待 主线程 等待 其余子线程
多线程的最主要特征之一是:同一进程中所有线程数据共享
一旦共享必然出现竞争问题。
线程中也存在安全问题,
多线程可以并发执行,一旦并发了并且访问了同一个资源就会有问题
案例:
rom threading import Thread, enumerate, Lock import time number = 10 lock = Lock() def task(): global number lock.acquire() a = number time.sleep(1) number = a - 1 lock.release() for i in range(10): t = Thread(target=task) t.start() for t in enumerate()[1:]: t.join() print(number)
死锁问题
当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源 要想使用必须同时具备两把锁 这时候程序就会进程无限卡死状态 ,这就称之为死锁 例如: 要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子 如何避免死锁问题 锁不要有多个,一个足够 如果真的发生了死锁问题,必须迫使一方先交出锁例子:
现有两把锁l1和l2 用于表示盘子和筷子
两个线程的目标是吃饭,要吃饭的前提是同时拿到筷子和盘子,但是两个人的目标不同一个先拿筷子 ,一个先拿盘子最终造成死锁
from threading import Lock, current_thread, Thread # 盘子 lock1 = Lock() # 筷子 lock2 = Lock() def eat1(): lock1.acquire() print("%s抢到了盘子" % current_thread().name) lock2.acquire() print("%s抢到了筷子" % current_thread().name) print("%s开吃了!" % current_thread().name) lock1.release() print("%s放下盘子" % current_thread().name) lock2.release() print("%s放下筷子" % current_thread().name) def eat2(): lock2.acquire() print("%s抢到了筷子" % current_thread().name) lock1.acquire() print("%s抢到了盘子" % current_thread().name) print("%s开吃了!" % current_thread().name) lock2.release() print("%s放下筷子" % current_thread().name) lock1.release() print("%s放下盘子" % current_thread().name) t1 = Thread(target=eat1) t2 = Thread(target=eat2) t1.start() t2.start()
Rlock 称之为递归锁 或者可重入锁 Rlock不使用用来解决死锁问题的 与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程已经执行过acquire 其他线程将无法执行acquire Rlock仅仅是帮你解决了代码逻辑上的错误导致的死锁,并不能解决多个锁造成的死锁问题
正常锁 from threading import RLock,Lock,Thread l=Lock() l.acquire() print('1111111') l.release() print('222222222')
死锁
from threading import RLock,Lock,Thread
l=Lock() l.acquire() print('111111') l.acquire() print('2222222') 在处理并发安全时 用完公共资源后一定要释放锁
Lock 锁住一个马桶 同时只能有一个 Semaphore 锁住一个公共厕所 同时可以来一堆人 用途: 仅用于控制并发访问 并不能防止并发修改造成的问题
from threading import Semaphore,Thread import time a=Semaphore(5) def task(): a.acquire() print('子线程。。') time.sleep(3) print('主线程。。') a.release() for i in range(8): t=Thread(target=task) t.start()