进程、线程补充与协程相关介绍

打印 上一主题 下一主题

主题 1015|帖子 1015|积分 3045

补充点

1.死锁

当你知道锁的使用抢锁必须要释放锁,其实你在操作锁的时候也极其容易产生死锁现象(整个程序卡死 阻塞)
  1. from threading import Thread, Lock
  2. import time
  3. mutexA = Lock()
  4. mutexB = Lock()
  5. # 类只要加括号多次 产生的肯定是不同的对象
  6. # 如果你想要实现多次加括号等到的是相同的对象 单例模式
  7. class MyThead(Thread):
  8.     def run(self):
  9.         self.func1()
  10.         self.func2()
  11.     def func1(self):
  12.         mutexA.acquire()
  13.         print('%s 抢到A锁'% self.name)  # 获取当前线程名
  14.         mutexB.acquire()
  15.         print('%s 抢到B锁'% self.name)
  16.         mutexB.release()
  17.         mutexA.release()
  18.         
  19.     def func2(self):
  20.         mutexB.acquire()
  21.         print('%s 抢到B锁'% self.name)
  22.         time.sleep(2)
  23.         mutexA.acquire()
  24.         print('%s 抢到A锁'% self.name)  # 获取当前线程名
  25.         mutexA.release()
  26.         mutexB.release()
  27. if __name__ == '__main__':
  28.     for i in range(10):
  29.         t = MyThead()
  30.         t.start()
复制代码
2.递归锁

递归锁的特点:
可以被连续的acquire和release
但是只能被第一个抢到这把锁执行上述操作
它的内部有一个计数器 每acquire一次计数加一 每realse一次计数减一
只要计数不为0 那么其他人都无法抢到该锁
  1. # 代码只需要将上述死锁的
  2. mutexA = Lock()
  3. mutexB = Lock()
  4. # 换成
  5. mutexA = mutexB = RLock()
  6. # 这样死锁的程序就可以正常运行下去
复制代码
3.信号量

信号量在不同阶段可能对应不同的技术点,在并发编程中,信号量指的是锁!!
信号量与互斥锁的比较:信号量可以看成是多个坑位,互斥锁只能是一个坑位!!
信号量的具体语法:
  1. from threading import Thread, Semaphore  # 信号量的模块
  2. import time,random
  3. sm = Semaphore(5)  # 表示同时开设5个坑位(互斥锁只开设一个)
  4. def task(name):
  5.     sm.acquire()  # 加锁
  6.     print('%s正在蹲坑'%name)
  7.     time.sleep(random.randint(1,3))  # 蹲坑时间不等
  8.     sm.release()  # 释放锁
  9. if __name__ == '__main__':
  10.     for i in range(20):  # 20个人去抢5个厕所
  11.         t = Thread(target=task,args=(i,))
  12.         t.start()
复制代码
4.Event事件

主进程/线程等待子进程/线程运行结束时,我们使用的是join方法!
而需要子进程/线程之间的相互的等待结束,需要用到event事件
  1. from threading import Thread,Event
  2. import time
  3. # 1.先产生一个event对象
  4. event = Event()
  5. def person():
  6.     print('你来了')
  7.     time.sleep(2)
  8.     event.set()  # 2.发送信号,表示我执行完毕了,你可以结束了
  9. def my():
  10.     event.wait()  # 3.接收到信号,好的我开始执行
  11.     print('我走了')
  12. if __name__ == '__main__':
  13.     t = Thread(target=person)
  14.     t1 = Thread(target=my)
  15.     t1.start()
  16.     t.start()
复制代码
5.进程池和线程池(重要掌握)

5.1 什么是池?

池是用来保证计算机硬件安全的情况下最大限度的利用计算机,它降低了程序运行的效率但是保证了计算机硬件的安全,从而让你的程序正常的运行!!
5.2 线程池的使用
  1. # 1.导入进程池/线程池模块
  2. from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
  3. import time
  4. # 2.创建一个线程池对象,该对象括号内不传参数,默认是开设当前cpu个数*5的线程
  5. pool = ThreadPoolExecutor(5) # 传入参数5,表示池子里固定创建了5个线程,这5个线程不会重复创建和销毁
  6. def task(name):
  7.     print('%s来了'% name)
  8.     time.sleep(2)
  9. # 3.朝池子中提交任务,第一个参数是函数名,第二个参数是函数需要传入的参数
  10. """
  11. 同步提交:提交任务之后原地等待返回结果,不往下执行任务
  12. 异步提交:提交任务之后不原地等待返回结果,继续往下执行
  13. """
  14. # pool.submit(task,'zhang') # 注意:这里的提交是一个异步提交
  15. # print('主')
  16. # 优化
  17. list = []
  18. for i in range(20): # 向池子中提交20个任务。池子中只有5个线程
  19.     res = pool.submit(task,i)  # 池方法异步提交之后有一个返回值,该返回值是一个future对象
  20.     # print(res.result())  # 该对象有一个result方法,可以用来返回对应线程函数的返回值,没有返回值则返回none
  21.     # result方法的使用使得submi异步提交变成了同步提交
  22.     list.append(res)
  23. # 如果想等待所有的线程全部执行完毕,在往下执行代码,可以使用池的shutdown方法
  24. pool.shutdown() # shutdown的作用是等待所有线程池运行完毕,再关闭所有线程池往下运行
  25. for i in list:
  26.     print(i.result())
复制代码
5.3 进程池的使用

基本同线程池,不同的是以下部分:
  1. # 1.导入进程池/线程池模块
  2. from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
  3. import time,os
  4. pool = ProcessPoolExecutor(5)  # 不传默认是cpu的个数
  5. def task(name):
  6.     print('%s来了'% name)
  7.     time.sleep(2)
  8. # call_back函数里面需要传一个参数,该参数就是res(一个future对象)
  9. def call_back(n):
  10.     # 可以通过n.result()的方法获取task函数的返回值
  11.     print(n.result())
  12. if __name__ == '__main__':
  13.     for i in range(20):
  14.         # add_done_callback方法是给submit这个异步提交添加一个回调机制,完成任务之后立刻回调结果,方法的参数是一个函数
  15.         res = pool.submit(task, i).add_done_callback(call_back)
复制代码
协程

1.什么是协程

协程其实就是一种在单线程下实现并发的操作,程序猿通过检测自己写的代码,一旦遇到了IO操作的代码,我们就编写代码进行切换,这样使得cpu一直处于运行状态,提高程序的运行效率!
2.协程的具体写法

gevent模块的引用时为了使得函数之间不断的切换+保存状态!!
  1. """
  2. 使用协程,就必须要我们通过代码实时检测程序是否进入到了IO操作,如果进入了IO操作,
  3. 就进行快速的切换,以此来实现并发的效果,提升程序的运行效率
  4. """
  5. # 1.导入gevent模块的spawn模块,该模块是用来监测函数里面的IO操作的,但是spawn模块是无法监测一些常见的IO操作的
  6. # 因此,需要导入gevent模块中的monkey模块,以此来监测所有的IO操作
  7. from gevent import monkey,spawn
  8. monkey.patch_all()  # 猴子补丁
  9. import time
  10. def func1():
  11.     print('hhh')
  12.     time.sleep(2)
  13.     print('lalala')
  14. def func2():
  15.     print('wwww')
  16.     time.sleep(3)
  17.     print('结束')
  18. start_time = time.time()
  19. g1 = spawn(func1) # spawn括号里是需要启动监测的函数名,函数名后面可以加需要传入的参数
  20. g2 = spawn(func2) # 该方法是异步提交,有一个返回值,如果没有join方法会自动往下执行代码,结束程序
  21. g2.join() # 等待函数执行完在往下执行
  22. g1.join()
  23. print(time.time()-start_time)
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

汕尾海湾

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表