昨日回顾1 生产者消费者-在生产者和消费者之间,通过队列,增加缓冲,避免了生产者和消费者之间交互-Queue,redis,rabbitmq,kafka-解耦合,队列是微服务的基础2 线程理论,开启-进程是资源分配的最小单位,线程是执行的最小单位(cpu调度的最小单位),每个进程中最少一个线程-两种方式(跟进程完全类似)3 join方法-等待子线程执行结束,线程对象.join()4 线程数据共享-不同线程,变量是可以共用的,查看和修改(数据错乱)5 线程对象其他方法-name:人为设置,有默认-ident:线程号-active_count():现在还存活多少线程-is_alive():此线程是否还存活6 线程互斥锁-不同线程要修改同一个数据,要加锁-让并行变成串行,牺牲了效率,保证了数据安全-悲观锁,乐观锁,分布式锁7 GIL-全局解释器锁:在cpython解释器内部有一把大锁,线程要执行,必须获取到这把锁-为什么要有它? python的垃圾回收机制是线程不安全的,所有所有线程要抢到GIL才能执行-cpython的多线程不是真正的多线程,同一时刻,只有一个线程在执行,不能利用多核优势-----以下只针对于cpython解释器-在单核情况下:-开多线程还是开多进程?不管干什么都是开线程-在多核情况下:-如果是计算密集型,需要开进程,能被多个cpu调度执行-如果是io密集型,需要开线程,cpu遇到io会切换到其他线程执行今日内容1 验证GIL锁的存在方式from threading import Threadfrom multiprocessing import Processdef task():while True:passif __name__ == '__main__':for i in range(6):# t=Thread(target=task)  # 因为有GIL锁,同一时刻,只有一条线程执行,所以cpu不会满t=Process(target=task)   # 由于是多进程,进程中的线程会被cpu调度执行,6个cpu全在工作,就会跑满t.start()2 GIL与普通互斥锁的区别1 GIL锁是不能保证数据的安全,普通互斥锁来保证数据安全from threading import Thread, Lockimport timemutex = Lock()money = 100def task():global moneymutex.acquire()temp = moneytime.sleep(1)money = temp - 1mutex.release()if __name__ == '__main__':ll=[]for i in range(10):t = Thread(target=task)t.start()# t.join()  # 这样会变成串行,不能这么做ll.append(t)for t in ll:t.join()print(money)3 io密集型和计算密集型-----以下只针对于cpython解释器-在单核情况下:-开多线程还是开多进程?不管干什么都是开线程-在多核情况下:-如果是计算密集型,需要开进程,能被多个cpu调度执行-如果是io密集型,需要开线程,cpu遇到io会切换到其他线程执行from threading import Threadfrom multiprocessing import Processimport time# 计算密集型def task():count = 0for i in range(100000000):count += iif __name__ == '__main__':ctime = time.time()ll = []for i in range(10):t = Thread(target=task)  # 开线程:42.68658709526062# t = Process(target=task)   # 开进程:9.04949426651001t.start()ll.append(t)for t in ll:t.join()print(time.time()-ctime)# io密集型def task():time.sleep(2)if __name__ == '__main__':ctime = time.time()ll = []for i in range(400):t = Thread(target=task)  # 开线程:2.0559656620025635# t = Process(target=task)   # 开进程:9.506720781326294t.start()ll.append(t)for t in ll:t.join()print(time.time()-ctime)4 死锁现象(哲学家就餐问题)# 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁# 单例模式:https://www.cnblogs.com/liuqingzheng/p/10038958.html# 死锁现象,张三拿到了A锁,等B锁,李四拿到了B锁,等A锁from threading import Thread, Lockimport timemutexA = Lock()mutexB = Lock()def eat_apple(name):mutexA.acquire()print('%s 获取到了a锁' % name)mutexB.acquire()print('%s 获取到了b锁' % name)print('开始吃苹果,并且吃完了')mutexB.release()print('%s 释放了b锁' % name)mutexA.release()print('%s 释放了a锁' % name)def eat_egg(name):mutexB.acquire()print('%s 获取到了b锁' % name)time.sleep(2)mutexA.acquire()print('%s 获取到了a锁' % name)print('开始吃鸡蛋,并且吃完了')mutexA.release()print('%s 释放了a锁' % name)mutexB.release()print('%s 释放了b锁' % name)if __name__ == '__main__':ll = ['egon', 'alex', 'tie_dan']for name in ll:t1 = Thread(target=eat_apple, args=(name,))t2 = Thread(target=eat_egg, args=(name,))t1.start()t2.start()# 递归锁(可重入),同一个人可以多次acquire,每acquire一次,内部计数器加1,每relaese一次,内部计数器减一# 只有计数器不为0,其他人都不获得这把锁from threading import Thread, Lock,RLockimport time# 同一把锁# mutexA = Lock()# mutexB = mutexA# 使用可重入锁解决(同一把锁)# mutexA = RLock()# mutexB = mutexAmutexA = mutexB =RLock()def eat_apple(name):mutexA.acquire()print('%s 获取到了a锁' % name)mutexB.acquire()print('%s 获取到了b锁' % name)print('开始吃苹果,并且吃完了')mutexB.release()print('%s 释放了b锁' % name)mutexA.release()print('%s 释放了a锁' % name)def eat_egg(name):mutexB.acquire()print('%s 获取到了b锁' % name)time.sleep(2)mutexA.acquire()print('%s 获取到了a锁' % name)print('开始吃鸡蛋,并且吃完了')mutexA.release()print('%s 释放了a锁' % name)mutexB.release()print('%s 释放了b锁' % name)if __name__ == '__main__':ll = ['egon', 'alex', 'tie_dan']for name in ll:t1 = Thread(target=eat_apple, args=(name,))t2 = Thread(target=eat_egg, args=(name,))t1.start()t2.start()5 Semaphore信号量# Semaphore:信号量可以理解为多把锁,同时允许多个线程来更改数据from  threading import Thread,Semaphoreimport timeimport randomsm=Semaphore(3) # 数字表示可以同时有多少个线程操作def task(name):sm.acquire()print('%s 正在厕所位'%name)time.sleep(random.randint(1,5))sm.release()6 Event事件# 一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号# 比如一个线程等待另一个线程执行结束再继续执行# 一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号# 比如一个线程等待另一个线程执行结束再继续执行from threading import Thread, Eventimport timeevent = Event()def girl(name):print('%s 现在不单身,正在谈恋爱'%name)time.sleep(10)print('%s 分手了,给屌丝男发了信号'%name)event.set()def boy(name):print('%s 在等着女孩分手'%name)event.wait()  # 只要没来信号,就卡在者print('女孩分手了,机会来了,冲啊')if __name__ == '__main__':lyf = Thread(target=girl, args=('刘亦菲',))lyf.start()for i in range(10):b = Thread(target=boy, args=('屌丝男%s号' % i,))b.start()# 作业:起两个线程,第一个线程读文件的前半部分,读完发一个信号,另一个进程读后半部分,并打印from threading import Thread, Eventimport timeimport osevent = Event()# 获取文件总大小size = os.path.getsize('a.txt')def read_first():with open('a.txt', 'r', encoding='utf-8') as f:n = size // 2  # 取文件一半,整除data = f.read(n)print(data)print('我一半读完了,发了个信号')event.set()def read_last():event.wait()  # 等着发信号with open('a.txt', 'r', encoding='utf-8') as f:n = size // 2  # 取文件一半,整除# 光标从文件开头开始,移动了n个字节,移动到文件一半f.seek(n, 0)data = f.read()print(data)if __name__ == '__main__':t1=Thread(target=read_first)t1.start()t2=Thread(target=read_last)t2.start()7 线程queue# 进程queue和线程不是一个# from multiprocessing import Queue# 线程queuefrom queue import Queue,LifoQueue,PriorityQueue# 线程间通信,因为共享变量会出现数据不安全问题,用线程queue通信,不需要加锁,内部自带# queue是线程安全的'''三种线程Queue-Queue:队列,先进先出-PriorityQueue:优先级队列,谁小谁先出-LifoQueue:栈,后进先出'''如何使用q=Queue(5)q.put("lqz")q.put("egon")q.put("3号")q.put("4号")q.put("5号")# q.put("银dan")# q.put_nowait("银dan")# 取值print(q.get())print(q.get())print(q.get())print(q.get())print(q.get())# 卡住# print(q.get())# q.get_nowait()# 是否满,是否空print(q.full())print(q.empty())LifoQueueq=LifoQueue(5)q.put("lqz")q.put("egon")q.put("3号")q.put("4号")q.put("5号")## q.put("ddddan")print(q.get())PriorityQueue:数字越小,级别越高q=PriorityQueue(3)q.put((-10,'1号'))q.put((100,'2号'))q.put((101,'3号'))# q.put((1010,'铁dddan'))  # 不能再放了print(q.get())print(q.get())print(q.get())8 线程池1 为什么会出现池?不管是开进程还是开线程,不能无限制开,通过池,假设池子里就有10个,不管再怎么开,永远是这10个2 如何使用from concurrent.futures import ThreadPoolExecutorpool = ThreadPoolExecutor(2)pool.submit(get_pages, url).add_done_callback(call_back)from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorfrom threading import Threadimport timeimport randompool = ThreadPoolExecutor(5)  # 数字是池的大小# pool = ProcessPoolExecutor(5)  # 数字是池的大小def task(name):print('%s任务开始' % name)time.sleep(random.randint(1, 4))print('任务结束')return '%s 返回了'%namedef call_back(f):# print(type(f))print(f.result())if __name__ == '__main__':# ll=[]# for i in range(10):  # 起了100个线程#     # t=Thread(target=task)#     # t.start()#     res = pool.submit(task, '屌丝男%s号' % i)  # 不需要再写在args中了#     # res是Future对象#     # from  concurrent.futures._base import Future#     # print(type(res))#     # print(res.result())  # 像join,只要执行result,就会等着结果回来,就变成串行了#     ll.append(res)## for res in ll:#     print(res.result())# 终极使用for i in range(10):  # 起了100个线程# 向线程池中提交一个任务,等任务执行完成,自动回到到call_back函数执行pool.submit(task,'屌丝男%s号' % i).add_done_callback(call_back)from concurrent.futures import ThreadPoolExecutorimport requests  # 爬虫会学到的模块pool = ThreadPoolExecutor(2)def get_pages(url):# https://www.baidu.comres = requests.get(url)  # 向这个地址发送请求name = url.rsplit('/')[-1] + '.html'print(name)  # www.baidu.com.html# res.content拿到页面的二进制return {'name': name, 'text': res.content}def call_back(f):dic = f.result()with open(dic['name'], 'wb') as f:f.write(dic['text'])if __name__ == '__main__':ll = [这里面放网站链接]for url in ll:pool.submit(get_pages, url).add_done_callback(call_back)9 进程池1 如何使用from concurrent.futures import ProcessPoolExecutorpool = ProcessPoolExecutor(2)pool.submit(get_pages, url).add_done_callback(call_back)============================其余补充===================================进程 与 线程 的数据 比较:不同的 进程,变量 不能共用, 仅可以查看主进程中的变量,但是不能修改线程的数据(即 主线程中的变量)共享:    不同线程,变量是可以共用的,查看和修改(数据错乱)多线程 与 多进程 的区别:1、开启多个进程时: 父进程有一个pid, 每一个子进程各有一个pid即创建一个 父进程,创建若干个 子进程2、开启多个线程时:所有的线程(主线程 、子线程) 只有一个pid即创建一个新进程,盛放 所有的所有的线程对于cpython解释器:多线程:程序只能在 单 个cpu上, 并发 执行多进程:程序可在  多 个cpu上, 并行 执行----应用: 在各种情况下,究竟是开 多进程  or  多线程对于 单 核计算机:由于在一个cpu上都只能进行并发执行,并且线程的开销比进程更小,所以程序无论是干什么,都是开多线程开多线程还是开多进程?不管干什么都是开线程对于 多 核计算机:对于 计算密集型程序: 需要开 多进程, 能被放在多个cpu上调度执行对于 I/O密集型程序: 由于 存在着频繁的I/O阻塞, 为了系统资源的最大化,只需要开 多线程, 放在一个cpu上进行并发执行即可。如果使用多进程方式, 系统资源消耗更大, 运行所需时间反而会更长,因为 cpu频繁调度进程时,会耗费大量的时间,用来调度进程的资源 解释性的语言,  线程是 不安全的因为是边解释边执行的,不知道当前数据是否是之后用到的所以使用了GIL锁,用于将当前的程序中的所有数据都固定住,不被垃圾回收机制回收掉编译型的语言,  线程 是安全的因为一次性编译之后,知道哪些数据是需要用到的,对于用不到的则进行回收乐观锁 与 悲观锁:悲观锁:能保证第一个修改成功从一开始,就认为本次 有其他人对数据进行修改,所以一开始就进行加锁乐观锁:不能保证修改成功从一开始,就认为本次 没有数据进行修改,所以一开始就不进行加锁,但当真正要改的时候,如果发现数据被改动了,则取消本次修改课堂例题中:上面一个  for  i  in range(10):...是完完全全的串行下面一个 for  t in  l1: 变成了并行此时没有sleep时,并行之后就需要,用到锁死锁问题: 互相等待对方手中的资源多线程时:由于多个线程只能在一个cpu上执行,一个cpu不是执行它,就是执行另一个,所以一旦,双方都需要对方手中的资源,则就会陷入互相等待的局面递归锁: 把并发的程序,  变成了  串行的程序Event事件event.wait()    # 当前需要等待事件发生,然后再进行下一步evert.set()     # 当前 事件发生,可以进行之后的操作三种线程Queue-Queue:队列,先进先出-PriorityQueue:优先级队列,谁小谁先出# q=PriorityQueue(3)优先级队列特点: 放入值的时候,需要指定优先级# q.put((-10,'金蛋'))-LifoQueue:栈,后进先出队列,后进先出queue注意点:# 进程  中的队列  queue (进程queue和线程不是一个)from multiprocessing import Queue# 线程    queue 是安全的from queue import Queue,LifoQueue,PriorityQueue# 线程之间通信,因为共享变量会出现数据不安全问题# 用线程queue,不需要加锁,内部自带; 线程queue是安全的    进程  和 线程中都有,  Queue 但是 他们的Queue不是同一个东西, 不能混着用使用并发的 tcp方式 的socket通信以下代码需要写在if __name__ == '__main__':的里面,否则每当开启一个进程的时候,__main__ 上面的所有代码,就会被搬运在一个新文件中执行一次,而为服务端指定的ip:端口  是固定的,所以就会出现当前的ip:端口  被占用的报错使用万能异常的时候,不光要保证即使有错,还能继续执行还要能看到程序究竟出现了什么错误,因此以后应按照下面这种方式进行书写,即出现了异常 也要打印出异常except Exception as e:print(e)break客户端如果发送一个内容固定的字符串时,也可以使用这种方式,即 直接将字符串内容指定为 utf-8编码的byte类型client.send(b'hello world')线程池 、进程池 要点:1.from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutorprocess_pool = ProcessPoolExecutor( pool_size )   # 进程池thread_pool = ThreadPoolExecutor( pool_size )   # 线程池2. 普通方式开启进程/线程,无法获得task中的返回值{t=Thread(target=task, args=(f'屌丝男 {i} 号', ))t.start()仅仅是效果 等效于res=pool.submit(task, f'屌丝男 {i} 号' )这里的res内容:  <Future at 0x26367ad1d00 state=running><Future at 0x26367ae9d00 state=pending>3. 多进程/多线程 中的任务   带返回值时:   通过 call_back(f) 方法,获取每个进程的结果{向线程池中提交一个任务,等任务执行完成,自动回到call_back函数执行pool.submit(task,f'屌丝男 {i}号').add_done_callback(call_back)  # 启动进程 、把进程中任务的结果返回call_back函数自行定义:def call_back(f):# print(type(f))  # 观察其 类型print(f.result())}5.{第一种: res=pool.submit(task, f'屌丝男 {i} 号' )print(res.result())  # 像join,只要执行result,就会等着结果回来,就变成串行了表面上类似于下面这种形式,不过上面这种 就变成 串行的了,而下面的则仍然是并发 第二种:pool.submit(task, f'屌丝男 {i} 号' ).add_done_callback(call_back)仍然是并发运行各个程序(不是串行!!! 和原来无返回值一样),并且在运行完之后, task返回的结果 就 传给了call_back, 在call_back中可以对返回的结果进行处理 }==============================================GIL锁(即 解释器的锁,  一次只能让一个线程获得运行的权限,在解释器上运行) 加自己的mutex锁,使得数据一次完整的操作,只能是一个线程进行正常情况,都先释放后获得的锁可重入: 一个人可以多次获得同一把锁sm 用于  限制同时 用厕所的人的个数使用共享变量,会出现数据错乱的问题  所以推荐使用 队列windows中  开多进程时,一定要把所有的代码都写到 ...main... 中将所有的 进程/线程对象t,都装进一个列表中,目的:为了这些线程都执行完, 最终查看 外部变量的结果# 开辟相同数量的 进程 和 线程,进程的花销比线程要大

8-26-GLI锁与普通互斥锁、死锁问题、递归锁、信号量、Event事件、并发的tcp通信、进程池线程池相关推荐

  1. 互斥锁、死锁、递归锁、信号量、Event

    互斥锁 死锁和递归锁 Semaphore信号量 Event事件 互斥锁 互斥锁也叫用户锁.同步锁. 在多进程/多线程程序中,当多个线程处理一个公共数据时,会有数据安全问题,唯一能保证数据安全的,就是通 ...

  2. 11.python并发入门(part4 死锁与递归锁)

    一.关于死锁. 死锁,就是当多个进程或者线程在执行的过程中,因争夺共享资源而造成的一种互相等待的现象,一旦产生了死锁,不加人工处理,程序会一直等待下去,这也被称为死锁进程. 下面是一个产生" ...

  3. GIL+死锁与递归锁+信号量+event事件

    GIL全局解释器锁: GIL本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL,同一进程内的多个线程 必须抢到GIL之后才能使用Cpython解释器来执行自己的代码,即同一进程下的多个线 ...

  4. python 线程死锁_python线程死锁与递归锁

    死锁现象 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去. 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待 ...

  5. 7.3.7 - 并发多线程 死锁和递归锁

    一 死锁现象 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等 ...

  6. 递归锁、信号量、GIL锁、基于多线程的socket通信和进程池线程池

    递归锁.信号量.GIL锁.基于多线程的socket通信和进程池线程池 递归锁 死锁现象:是指两个或两个以上的进程和线程因抢夺计算机资源而产生的一种互相等待的现象 from threading impo ...

  7. 学习笔记(28):Python网络编程并发编程-死锁与递归锁

    立即学习:https://edu.csdn.net/course/play/24458/296445?utm_source=blogtoedu 1.死锁(Lock()的局限性) 知识点:Lock()只 ...

  8. python queue死锁_Python学习【第24篇】:死锁,递归锁,信号量,Event事件,线程Queue...

    一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统 ...

  9. Python之进程+线程+协程(并发与并行、GIL锁、同步锁、死锁、递归锁)

    文章目录 一.并发与并行 二.同步与异步 三.线程锁 1.GIL全局解释器锁 2.同步锁 3.死锁 4.递归锁 在Python中GIL解释器锁.同步锁.死锁.递归锁都是什么?怎么这么多锁,它们都是用来 ...

  10. python中的多线程 GIL(全局解释器锁) 死锁与递归锁

    1.什么的是线程 在程序里一个执行路线就叫做线程,线程是程序执行的最小单位 2.多线程的优点 使用线程可以把占据长时间的程序中的任务放到后台去处理. 在处理I/O密集程序的运行速度可能加快(ps:计算 ...

最新文章

  1. pandas数据索引之loc、iloc、ix详解及实例
  2. 重磅!监管再升级!微信、淘宝、抖音或将纳入“超级平台”监管
  3. mysql 原理 ~ DDL之在线DDL
  4. 给力分享新的ORM = Dapper( 转)
  5. Retrofit2 multpart多文件上传详解
  6. Web前端开发应该避免的几个思维误区
  7. MATLAB学习笔记(八)
  8. 异常和中断处理流程: Exception- or Interrupt-Handler Procedures
  9. idea 关闭检查更新_Intellij idea的抑制警告(SuppressWarnings)列表(正在持续更新)
  10. Android ImageButton单击切换按钮图片效果
  11. Python3 PyV8“安装与使用”教程
  12. 使用springboot,Oauth2.0,jwt令牌实现单点登录,权限控制等功能的基本流程
  13. python源码解读之 string.py
  14. Linux内核“问题门”——学习问题、经验集锦(持续更新中……)
  15. java中long最大值源码表示_通过JDK源码角度分析Long类详解
  16. TCP/IP之大明王朝邮差
  17. statsmodels遇到的坑!!!
  18. Matlab基本初等函数大全
  19. 鸿蒙系统安全模式,华为鸿蒙OS上线!开源开放毫无保留,这一友商早已用上鸿蒙同款系统...
  20. 我认为ACT游戏开发必用的程序设计模式!!!

热门文章

  1. 这家国内最早的区块链公司6年来做了什么?
  2. 计算机绘本教程,宝宝语言启蒙培生幼儿英语教程K1~K4全144册绘本PDF+音频+电脑EXE程序...
  3. 电商直播的带货技巧有哪些?
  4. Skr-Eric的网络编程课堂(八)-- 信号通信、信号量、进程的同步互换和Lock锁
  5. 什么是用户体验设计?
  6. 第一模块 商务邮件写作到底有多重要
  7. pip安装本地指定版本的whl文件
  8. 【Pandas学习】读、存excel数据
  9. VirtualCalle-虚拟来电恶作剧开源版
  10. 用C++来实现关系矩阵的性质(自反性、反自反性、对称性、反对称性、)的判断