day 35 线程
内容回顾
# 互斥锁 #在同一个进程中连续锁多次 #进程的数据共享 #进程之间可以共享数据 #提供共享数据的类是Manager #但是它提供的list|dict 这些数据类型 #针对+= -= *= -= #需要加锁保证安全 # 进程之间的通信 # ipc: #queue #管道 #机制也可以数据通信 #manager #lock #acquire acquire 还有一块空间 有锁的状态 #Process #开启时 # 进程锁 线程锁 #一个共享 一个锁就得通信 线程锁不锁得住 进程 # 线程 #概念 #进程和线程的区别 #进程 开销大 数据隔离 #是计算机中最小的资源分配单位 #线程 轻量级 共享数据 #是计算机中能被cpu调度的最小单位 #正常的线程是什么 #能同时被多个cpu执行 #Cpython解释器下的线程(解释器里有 锁只一个进程一个时刻会用) #GIL锁 #全局解释器锁 #是CPython解释其中的 #会导致同一个时刻只能有一个线程 #代码 threading 模块 #Thread 类 #开启线程 #传参数 #没有terminate #join #active_count int 当前程序中正在执行的线程个数 #current_thread 线程对象 能够获取 当前线程 的对象 from multiprocessing import Process class Myprocess(Process): def __init__(self,args): super().__init__() self.args = args def run(self): print('子进程执行',self.name) p1 = Myprocess(1) p1.start() #self指向p1 和p1 是一样的 #和 current_thread一样 p1.join() p2 = Myprocess(1) p2.start() #self指向p2 #水性杨花 p2.join()
今日内容
# 守护线程
# threading.enumerate
# 线程锁
#互斥锁
#递归锁
#死锁现象
#线程队列
#进程池和线程池
3 不大对的enumeate 方法
#__author : 'liuyang' #date : 2019/4/18 0018 上午 9:24 from threading import Thread, enumerate,active_count def func(): print('in son thread') Thread(target= func).start() print(enumerate()) #[<_MainThread(MainThread, started 2788)>] active_count = len(enumerate()) print(active_count)
老师讲
from threading import enumerate,Thread def func(): print('in son thread') Thread(target=func).start() print(enumerate()) # 返回一个存储着所有线程对象的列表 # active_count = len(enumerate()) # [<_MainThread(MainThread, started 1200)>, # <Thread(Thread-1, started 4156)>]
4守护线程
#__author : 'liuyang' #date : 2019/4/18 0018 上午 9:27 from threading import Thread import time def daemon_func(): while True: time.sleep(0.5) print('守护线程') def son_func(): print("start son") time.sleep(5) print('end son') t = Thread(target=daemon_func) t.daemon = True t.start() Thread(target=son_func).start() time.sleep(3) print('主线程结束了') # 1. 主线程会等待子线程的结束而结束 # 2. 守护线程会随着主线程的结束而结束 #守护线程会守护主线程 和 所有的子线程 # 进程会随着 主线程的结束而结束 # 所有非守护线程终止,即使存在守护线程,进程运行终止 # 所有守护线程终止, # 问题 #1. 主线程需不需要回收子线程的资源 #不需要 , 线程资源属于进程, 所以进程结束了,线程的资源自然就被回收了 # 2 .主线程 为什么要等待子线程结束之后结束 # 主线程结束意味着进程结束,所有的子线程都会结束 # 要想让子线程能够顺利执行完,主线程只能等 #3 守护线程 到底要怎么结束的 #主线程结束了 进程也结束了 守护线程被主进程的结束结束掉了(守护了这个进程。。。) #守护进程: 只会守护到主进程的代码结束 #守护线程: 会守护到 所有其它非守护线程的结束
5 线程
还要加锁 ? GIL 的锁为了节省时间 没有很负责的锁住 浪费时间 只锁规定时间内(时间片)
#__author : 'liuyang' #date : 2019/4/18 0018 上午 10:10 # 线程里有必要要锁么? # GIL 和 锁的关系 #轮流执行一个 cpu from dis import dis from threading import Thread,Lock count = 0 # count = [] l = [] def add_func(lock): global count for i in range(200000): with lock : count += 1 # count.append(1) def sub_func(lock): global count with lock: for i in range(200000): count -= 1 # i = 200000 # while i >0 : # if count: # count.pop() # i -= 1 def add_list(): l.append(1) # dis(add_func) t_l = [] lock = Lock() # for i in range(5): # t1 =Thread(target=add_func,args = (lock,)) # t1.start() # t_l.append(t1) # t2 =Thread(target=sub_func,args= (lock,)) # t2.start() # t_l.append(t2) # for t in t_l:t.join() # print(count) dis(add_list) # 数据不安全 ''' 两个线程模拟 加载count 1 加载+ 存回全局变量 14 14 LOAD_GLOBAL 1 (count) 16 LOAD_CONST 2 (1) 18 INPLACE_ADD 20 STORE_GLOBAL 1 (count) 22 JUMP_ABSOLUTE 10 >> 24 POP_BLOCK >> 26 LOAD_CONST 0 (None) 28 RETURN_VALUE''' # 当执行 STORE += 两步操作 # GIL 时间片 轮转 (700条 ):造成了数据不安全 #锁保证一个时间短一个线程 但是 没执行完 没存值 #不是python代码 而是底层cpu执行 # l.append(1) ''' lst.append(1) 原子性:不可再分性 [1,1] 30 0 LOAD_GLOBAL 0 (l) 2 LOAD_ATTR 1 (append) 4 LOAD_CONST 1 (1) 6 CALL_FUNCTION 1 # 调函数 8 POP_TOP #POP :出栈 #pop是弹出栈顶元素,top是获得栈顶元素,不弹出 10 LOAD_CONST 0 (None) 12 RETURN_VALUE''' # if [].pop() #两步操作 轮转了 改变了 没法pop了 所以报错 # += #两步操作 轮转了 没改变全局变量存到里面 所以有重叠 # 数据不安全 #在线程中也会出现数据不安全的 #1.对全局变量进行修改 #2.对某个值 += -= *= /= # 通过加锁来解决 # 设计都写好的函数 修改的 # list pop append extend insert insert remove # dict pop update # list[0] += 1 # dic[key] -= 1 #list.pop/append # pop列表为空时会 报错 # queue put/get # get队列为空时会 等待 # GIL # # 如果没有GIL锁 # append(1) append(1) 多个cpu同时进行 # 在一个列表地址中 被覆盖了 # 1 . 有了GIL 保证了线程同一时刻只能有一个线程访问CPU,不可能有两个线程t同时在cpu上执行指令 # 没有GIL操作 任何小操作都得加锁 # 2 lock 锁 保证某一段代码 在没有执行完毕之后,不可能有另一个线程也执行它 # 时间片轮转
6 科学家吃面问题
两个人 两个东西 得二可得天下 但是一人一个
#__author : 'liuyang' #date : 2019/4/18 0018 上午 10:54 #死锁: 永远解不开了才叫 # acquire # acquire 低级的死锁 好开 # ''' from threading import Lock # 死锁代码 :操作两个变量的 两个函数同时使用两个变量 时机问题 我有叉子 你有面 # 自己代码没问题 没测到并发 很多线程 上线了服务 from threading import Thread #用户一多 多线程 死锁 阻在那了 不走了 noodle_lock = Lock() fork_lock = Lock() import time def eat1(name): noodle_lock.acquire() print('%s 拿到面了'%name) fork_lock.acquire() print('%s 拿到叉子'%name) print('%s 吃面'%name) time.sleep(0.1) fork_lock.release() print('%s 放下茶子'%name) noodle_lock.release() print('%s 放下面'%name) def eat2(name): fork_lock.acquire() print('%s 拿到叉子了'%name) noodle_lock.acquire() print('%s 拿到面了'%name) print('%s 吃面'%name) noodle_lock.release() print('%s 放下面' % name) fork_lock.release() print('%s 放下茶子'%name) Thread(target=eat1 , args=('alex',)).start() Thread(target=eat2 , args=('al',)).start() Thread(target=eat1 , args=('ex',)).start() # ''' # 快速解决问题 #改成一把锁 #递归锁 # 门口挂着一串(万能)钥匙 (互斥一个钥匙) #也是把两把锁并成 一把锁 ,别的结构不改 # 很多个门 #lock.acquire() #拿到面了 #lock.acquire() #拿到叉子 #吃面 #lock.release() #放下面 #lock.release() #放下叉子 #归还到最外层 # # from threading import RLock , Lock # lock = Lock() # rlock = RLock() # lock.acquire() # print(123) # lock.acquire() # print(456) # rlock.acquire() # print(123) # rlock.acquire() # print(456) # rlock.acquire() # print(56) #万能钥匙 可以近很多个锁门 但是得出来 # rlock.release() # rlock.release() # rlock.release() # 搞两个锁都会锁 不管递归锁 # 在同一个线程中是不会出现数据安全(死锁) 的 所以 递归锁 可以 from threading import RLock , Lock,Thread # fork_lock = noodle_lock = RLock() # def eat1(name): # noodle_lock.acquire() # print('%s 拿到面了'%name) # fork_lock.acquire() # print('%s 拿到叉子'%name) # print('%s 吃面'%name) # fork_lock.release() # print('%s 放下茶子'%name) # noodle_lock.release() # print('%s 放下面'%name) # def eat2(name): # noodle_lock.acquire() # print('%s 拿到叉子了'%name) # fork_lock.acquire() # print('%s 拿到面子'%name) # print('%s 吃面'%name) # fork_lock.release() # print('%s 放下茶子'%name) # noodle_lock.release() # print('%s 放下面'%name) # Thread(target=eat1 , args=('l',)).start() # Thread(target=eat1 , args=('y',)).start() # lock = Lock() #上面的效率低 改成 互斥锁 # def eat1(name): #最简单 没问题 # lock.acquire() # print('%s 拿到面了'%name) # print('%s 拿到叉子'%name) # print('%s 吃面'%name) # print('%s 放下茶子'%name) # print('%s 放下面'%name) # lock.release() # def eat2(name): # lock.acquire() # print('%s 拿到面了'%name) # print('%s 拿到叉子'%name) # print('%s 吃面'%name) # print('%s 放下茶子'%name) # print('%s 放下面'%name) # lock.release() # Thread(target=eat1 , args=('l',)).start() # Thread(target=eat1 , args=('y',)).start() #两个进程 各拿到一个 钥匙 #都完不成 # 你要操作的数据不止一个 a = 1 b = 2 c = a+b # a.acquire() #忘了解锁 阻塞在某个点了 可能锁了 # b.acquire() # a.release() # b.release() # 出错了 改不出来了 就先把所有的锁 改成一个锁 没问题,但是效率不高 # 改好了, 再改一把把锁 # 获取锁 释放锁 不要把大段代码 放了锁里 #一把锁锁一个资源 # 死锁不是锁 #互斥锁 #递归锁
7.队列
#__author : 'liuyang' #date : 2019/4/18 0018 下午 12:08 from queue import Queue # Queue 就是一个线程队列的类, 自带 lock锁,实现了线程安全的数据类型 # 队列是一个线程安全的数据类型 放进去拿出来 # [][]+=1 不安全 列表 q = Queue() q.put(1) #在多线程下都不准 异步没法控制 q.empty() #判断是否为空 q.full() #判断是否为满 q.qsize() #队列的大小 q.put({1,2,3}) q.get() q.put_nowait({'abc'}) print(q.get_nowait()) #先进先出 print(q.get_nowait()) #先进后出 的 队列 last in first out from queue import LifoQueue #栈 后进先出的时候 都可以用过 lfq = LifoQueue() lfq.put(1) lfq.put('abc') lfq.put({'1','2','3','4'}) print(lfq.get()) print(lfq.get()) print(lfq.get()) # 栈从空间复杂度上来讲 栈的效率要比递归高 # 队列来讲顺序性 from queue import PriorityQueue #小的优先级高1 优先级队列 pq = PriorityQueue() pq.put((1,'abcd')) pq.put((2,'dcba')) pq.put((20,'dddd')) print(pq.get()) print(pq.get()) print(pq.get()) #设置成会员 抢票好 # 线程+队列 实现消费者生产者模型
8池 进程
#__author : 'liuyang' #date : 2019/4/18 0018 下午 12:08 from queue import Queue # Queue 就是一个线程队列的类, 自带 lock锁,实现了线程安全的数据类型 # 队列是一个线程安全的数据类型 放进去拿出来 # [][]+=1 不安全 列表 q = Queue() q.put(1) #在多线程下都不准 异步没法控制 q.empty() #判断是否为空 q.full() #判断是否为满 q.qsize() #队列的大小 q.put({1,2,3}) q.get() q.put_nowait({'abc'}) print(q.get_nowait()) #先进先出 print(q.get_nowait()) #先进后出 的 队列 last in first out from queue import LifoQueue #栈 后进先出的时候 都可以用过 lfq = LifoQueue() lfq.put(1) lfq.put('abc') lfq.put({'1','2','3','4'}) print(lfq.get()) print(lfq.get()) print(lfq.get()) # 栈从空间复杂度上来讲 栈的效率要比递归高 # 队列来讲顺序性 from queue import PriorityQueue #小的优先级高1 优先级队列 pq = PriorityQueue() pq.put((1,'abcd')) pq.put((2,'dcba')) pq.put((20,'dddd')) print(pq.get()) print(pq.get()) print(pq.get()) #设置成会员 抢票好 # 线程+队列 实现消费者生产者模型
9总结
# 守护线程
#锁
#互斥
#递归
#死锁现象
#队列 线程安全的数据
#先进先出 queue
#后进先出 LifoQueue
#优先级队列 PriorityQueue
#池
#控制进程的数量
#节省资源开销