1. 死锁与递归锁

死锁:两个或者两个以上的进程或者线程在执行过程中,因争夺资源而造成的一种等待现象,称为死锁现象。

递归锁可以解决死锁现象。

递归锁有一个计数的功能,原数字为0,锁一次计数+1,释放一次,计数-1;只要数字不为0,其他线程就不能枪锁。

  1. from threading import RLock
  2. from threading import Thread
  3. import time
  4. lock_A = lock_B = RLock() # 必须这样写
  5. class MyThread(Thread):
  6. def run(self):
  7. self.f1()
  8. self.f2()
  9. def f1(self):
  10. lock_A.acquire()
  11. print(f'{self.name}抢到了A锁!')
  12. lock_B.acquire()
  13. print(f'{self.name}抢到了B锁!')
  14. lock_B.release()
  15. lock_A.release()
  16. def f2(self):
  17. lock_B.acquire()
  18. print(f'{self.name}抢到了B锁!')
  19. time.sleep(0.1)
  20. lock_A.acquire()
  21. print(f'{self.name}抢到了A锁!')
  22. lock_A.release()
  23. lock_B.release()
  24. if __name__ == '__main__':
  25. for i in range(3):
  26. t = MyThread()
  27. t.start()

2. 信号量Semaphor

Semaphore管理一个内置的计数器,

每当调用acquire()时内置计数器-1;调用release() 时内置计数器+1;

计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

能够控制同一时刻的线程数。

  1. from threading import Thread, Semaphore, current_thread
  2. import time
  3. import random
  4. sem = Semaphore(5) # 设置同一时刻只能有5个线程并发执行
  5. def task():
  6. sem.acquire()
  7. print(f'{current_thread().name}进程在运行')
  8. time.sleep(random.random())
  9. sem.release()
  10. if __name__ == '__main__':
  11. for i in range(30):
  12. t = Thread(target=task)
  13. t.start()

3. GIL全局解释器锁:(Cpython)

​ Cpython规定,同一时刻只允许一个线程进入解释器,因为加了GIL。

​ 如果不加全局解释器锁,会出现死锁现象,开发此的程序员为了方便,在进入解释器时给线程加了一个全局解释器锁,这样保证了解释器内的数据安全

优点: 保证了Cpython解释器的数据资源的安全;

缺点: 单个进程的多线程不能利用多核cpu(缺陷)。

Jython、pypy解释器没有GIL全局解释器锁。

​ 流程:当线程遇到IO阻塞,cpu就会无情的被操作系统切走,GIL全局解释器锁被释放,线程挂起,另一个线程进入,这样可以实现并发。

总结

单个进程的多线程可以并发执行,但是不能利用cpu多核并行执行;

多个进程可以并发、并行执行。

4. IO、计算密集型对比

4.1 计算密集型:

​ 单个进程的多线程 VS 多个进程的并发、并行

  1. from threading import Thread
  2. from multiprocessing import Process
  3. import time
  4. def task():
  5. count = 0
  6. for i in range(10000000):
  7. count += 1
  8. # 多线程并发
  9. if __name__ == '__main__':
  10. start_time = time.time()
  11. l1 = []
  12. for i in range(4):
  13. p = Thread(target=task,)
  14. l1.append(p)
  15. p.start()
  16. for p in l1:
  17. p.join()
  18. print(f'执行效率:{time.time()- start_time}') # 1.9125
  19. # 多进程并发、并行
  20. if __name__ == '__main__':
  21. start_time = time.time()
  22. l1 = []
  23. for i in range(4):
  24. p = Process(target=task,)
  25. l1.append(p)
  26. p.start()
  27. for p in l1:
  28. p.join()
  29. print(f'执行效率:{time.time()- start_time}') # 0.86031
  30. # 总结:
  31. 计算密集型:多进程并发、并行效率高。

4.2 IO密集型

单个进程的多线程 VS 多个进程的并发并行

  1. from threading import Thread
  2. from multiprocessing import Process
  3. import time
  4. import random
  5. def task():
  6. count = 0
  7. time.sleep(random.randint(1,3))
  8. count += 1
  9. if __name__ == '__main__':
  10. # 多进程并发、并行
  11. start_time = time.time()
  12. l1 = []
  13. for i in range(50):
  14. p = Process(target=task,)
  15. l1.append(p)
  16. p.start()
  17. for p in l1:
  18. p.join()
  19. print(f'执行效率:{time.time()- start_time}') # 4.52878
  20. # 多线程并发
  21. start_time = time.time()
  22. l1 = []
  23. for i in range(50):
  24. p = Thread(target=task, )
  25. l1.append(p)
  26. p.start()
  27. for p in l1:
  28. p.join()
  29. print(f'执行效率:{time.time() - start_time}') # 3.00845
  30. # 总结:
  31. 对于IO密集型:单个进程的多线程的并发效率高。

5. GIL与Lock锁的区别

相同点:都是互斥锁;

GIL: 保护解释器内部的资源数据的安全,释放无需手动操作;

Lock:自定义锁,保护进程中的数据资源安全。需要手动操作。

6. 多线程实现socket通信

服务端每连接到一个客户端时,都会开启一个线程进行通信。

  1. # server端
  2. from threading import Thread
  3. import socket
  4. def accept():
  5. server = socket.socket()
  6. server.bind(("127.0.0.1", 8888))
  7. server.listen(5)
  8. while 1:
  9. conn, addr = server.accept()
  10. # 连接一个开启一个线程
  11. t = Thread(target=communication, args=(conn, addr))
  12. t.start()
  13. def communication(conn, addr):
  14. while 1:
  15. try:
  16. from_client_data = conn.recv(1024)
  17. print(f"来自客户端{addr}的消息:{from_client_data.decode('utf-8')}")
  18. to_client_data = input(">>>").strip().encode("utf-8")
  19. conn.send(to_client_data)
  20. except Exception:
  21. break
  22. conn.close()
  23. if __name__ == '__main__':
  24. accept()
  1. # client端
  2. import socket
  3. client = socket.socket()
  4. client.connect(('127.0.0.1', 8888))
  5. while 1:
  6. try:
  7. to_server_data = input(">>>").strip().encode("utf-8")
  8. client.send(to_server_data)
  9. from_server_data = client.recv(1024)
  10. print(f"来自客户端的消息:{from_server_data.decode('utf-8')}")
  11. except Exception:
  12. break
  13. client.close()

7. 进程池、线程池

以时间换取空间。

​ 概念:定义一个池子,在里面放上固定数量的进程(线程),有需求来了,就拿一个池中的进程(线程)来处理任务,等到处理完毕,进程(线程)并不关闭,而是将进程(线程)再放回池中继续等待任务。如果有很多任务需要执行,池中的进程(线程)数量不够,任务就要等待之前的进程(线程)执行任务完毕归来,拿到空闲的进程(线程)才能继续执行。也就是说,池中进程(线程)的数量是固定的,那么同一时间最多有固定数量的进程(线程)在运行。这样不会增加操作系统的调度难度,还节省了开闭进程(线程)的时间,也一定程度上能够实现并发效果。

官网:https://docs.python.org/dev/library/concurrent.futures.html

  1. from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
  2. import os
  3. import time
  4. import random
  5. def task(i):
  6. print(f"第{i}:{os.getpid()}开始执行!")
  7. time.sleep(random.random())
  8. if __name__ == '__main__':
  9. # 线程池
  10. t = ThreadPoolExecutor() # 开启线程池 不写默认最大处理20个线程
  11. for i in range(40):
  12. t.submit(task, i+1) # 开启线程
  13. # 进程池
  14. p = ProcessPoolExecutor() # 开启进程池 不写默认处理cpu个数的进程
  15. for i in range(20):
  16. p.submit(task, i+1)

示例:使用线程池实现socket通信

  1. # server端
  2. from concurrent.futures import ThreadPoolExecutor
  3. import socket
  4. def accept():
  5. server = socket.socket()
  6. server.bind(("127.0.0.1", 8888))
  7. server.listen(5)
  8. t = ThreadPoolExecutor(2) # 设置最大2个线程的线程池
  9. while 1:
  10. conn, addr = server.accept()
  11. t.submit(communication, conn, addr)
  12. def communication(conn, addr):
  13. while 1:
  14. try:
  15. from_client_data = conn.recv(1024)
  16. print(f"来自客户端{addr}的消息:{from_client_data.decode('utf-8')}")
  17. to_client_data = input(">>>").strip().encode("utf-8")
  18. conn.send(to_client_data)
  19. except Exception:
  20. break
  21. conn.close()
  22. if __name__ == '__main__':
  23. accept()
  1. # client端
  2. import socket
  3. client = socket.socket()
  4. client.connect(('127.0.0.1', 8888))
  5. while 1:
  6. try:
  7. to_server_data = input(">>>").strip().encode("utf-8")
  8. client.send(to_server_data)
  9. from_server_data = client.recv(1024)
  10. print(f"来自客户端的消息:{from_server_data.decode('utf-8')}")
  11. except Exception:
  12. break
  13. client.close()

python 36 进程池、线程池的更多相关文章

  1. 《转载》Python并发编程之线程池/进程池--concurrent.futures模块

    本文转载自Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mult ...

  2. python自带的线程池和进程池

    #python自带的线程池 from multiprocessing.pool import ThreadPool #注意ThreadPool不在threading模块下 from multiproc ...

  3. python并发编程-进程池线程池-协程-I/O模型-04

    目录 进程池线程池的使用***** 进程池/线程池的创建和提交回调 验证复用池子里的线程或进程 异步回调机制 通过闭包给回调函数添加额外参数(扩展) 协程*** 概念回顾(协程这里再理一下) 如何实现 ...

  4. Python学习之GIL&进程池/线程池

    8.6 GIL锁** Global interpreter Lock 全局解释器锁 实际就是一把解释器级的互斥锁 In CPython, the global interpreter lock, or ...

  5. Python并发编程05 /死锁现象、递归锁、信号量、GIL锁、计算密集型/IO密集型效率验证、进程池/线程池

    Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密集型效率验证.进程池/线程池 目录 Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密 ...

  6. Python标准模块--concurrent.futures 进程池线程池终极用法

    concurrent.futures 这个模块是异步调用的机制concurrent.futures 提交任务都是用submitfor + submit 多个任务的提交shutdown 是等效于Pool ...

  7. concurrent.futures模块(进程池/线程池)

    需要注意一下不能无限的开进程,不能无限的开线程最常用的就是开进程池,开线程池.其中回调函数非常重要回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去自己加 ...

  8. Python-GIL 进程池 线程池

    5.GIL vs 互斥锁(*****) 1.什么是GIL(Global Interpreter Lock) GIL是全局解释器锁,是加到解释器身上的,保护的就是解释器级别的数据 (比如垃圾回收的数据) ...

  9. 13 并发编程-(线程)-异步调用与回调机制&进程池线程池小练习

    #提交任务的两种方式 #1.同步调用:提交完任务后,就在原地等待任务执行完毕,拿到结果,再执行下一行代码,导致程序是串行执行 一.提交任务的两种方式 1.同步调用:提交任务后,就在原地等待任务完毕,拿 ...

  10. 并发编程---线程queue---进程池线程池---异部调用(回调机制)

    线程 队列:先进先出 堆栈:后进先出 优先级:数字越小优先级越大,越先输出 import queue q = queue.Queue(3) # 先进先出-->队列 q.put('first') ...

随机推荐

  1. vscode如何将less编译到指定css目录中

    首先使用vscode的搬砖猿,想要在vscode编辑器里面很方便的编译less文件,需要在扩展商店里面下载一款名叫Esay LESS的超好用扩展(我自己都不信),哈哈,其实还好. 安装完扩展之后,最好 ...

  2. 十一、SQL Server CONVERT() 函数

    定义和用法 CONVERT() 函数是把日期转换为新数据类型的通用函数. CONVERT() 函数可以用不同的格式显示日期/时间数据. 语法 CONVERT(data_type(length),dat ...

  3. 头部姿态估计 - OpenCV/Dlib/Ceres

    基本思想 通过Dlib获得当前人脸的特征点,然后通过旋转平移标准模型的特征点进行拟合,计算标准模型求得的特征点与Dlib获得的特征点之间的差,使用Ceres不断迭代优化,最终得到最佳的旋转和平移参数. ...

  4. Samba:打造企业级授权文件共享服务器

    写在前面的话 先来说说故事背景:公司内部文件服务器的解决方案其实很多,对于中小型互联网公司,大多的在这一块的选型还是 FTP,或者 VSFTP,但是个人实在是对那个东西喜欢不起来,于是就选择了配置相对 ...

  5. Reactv16.8.6生命周期函数

    组件生命周期函数 React 主动调用的方法,也可重写这些方法 生命周期图谱 当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下: constructor(props) 如果不需要初始化 s ...

  6. 关于STM32F103+ESP8266+阿里云过程之修改SDK支持UART和SmartConfig(四)

    设备上报状态到阿里云成功之后,还要接受来至云端下发的命令,如APP.在ESP8266接受到数据之后可将数据先进行解析,再通过自定义协议与STM32进行串口通讯,也可以将接收到的数据中的信息直接传输到U ...

  7. 面试必谈的哈希,.Net 程序员温故而知新

    引言: 作为资深老鸟,有事没事,出去面试:找准差距.定位价值. 面试必谈哈希, Q1:什么是哈希? Q2:哈希为什么快? Q3:你是怎么理解哈希算法利用空间换取时间的? Q4:你是怎么解决哈希冲突的? ...

  8. Apache Flink 1.9 重大特性提前解读

    今天在 Apache Flink meetup ·北京站进行 Flink 1.9 重大新特性进行了讲解,两位讲师分别是 戴资力/杨克特,zhisheng 我也从看完了整个 1.9 特性解读的直播,预计 ...

  9. Vue2.0仿饿了么webapp单页面应用

    Vue2.0仿饿了么webapp单页面应用 声明: 代码源于 黄轶老师在慕课网上的教学视频,我自己用vue2.0重写了该项目,喜欢的同学可以去支持老师的课程:http://coding.imooc.c ...

  10. Micropython TPYBoard v102 温湿度短信通知器(基于SIM900A模块)

    前言 前段时间看了追龙2,感受就是如果你是冲着追龙1来看追龙2的话,劝你还是不要看了,因为追龙2跟追龙1压根没什么联系,给我的感觉就像是看拆弹专家似的,估计追龙2这个名字就是随便蹭蹭追龙1的热度来的. ...