python并发编程
2019-07-24 09:13:45来源:博客园 阅读 ()
并发编程是我们编程中常用的优化程序性能的手段,能提高CPU的使用率。一般使用是多线程,多进程,协程
一、python的全局解释锁GIL
我们目前跑的python程序大多数都是在cpython上运行的。cpython是有一个全局解释锁,具体什么意思,可以两个方面理解
- 在同一时刻,只能运行一个线程
- 无法将多个线程映射到多个cpu上运行
也就是说python多线程是有一定局限性的,对于io密集型的任务,我们可以很好的利用多线程,对于计算密集型的任务,多线程就没有意义了,只能利用多进程提高性能
二、简单多线程编程
第一种形式:使用threading库,直接使用Thread类
from threading import Thread import time def fun(n): # do something time.sleep(n) print(n) t1 = Thread(target=fun, args=(1,)) t2 = Thread(target=fun, args=(2,)) # 启动线程 t1.start() t2.start() # 等待线程结束 t1.join() t2.join()
第二种形式:使用threading库,继承Thread类
from threading import Thread import time class MyThread(Thread): def __init__(self, n): self.n = n super().__init__() def run(self): time.sleep(self.n) print(self.n) t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start() t1.join() t2.join()
三、线程安全
多个线程在同一个进程中运行时,是共享内存空间的,这就有可能引起一个线程在修改一个变量到一半的时候去休息了,回来的时候发现这个变量变了,这就会产生问题
如何保证线程安全,一般有下面几个方法
- 使用私有变量,每个线程都只知道自己的变量,其他线程无法修改,一般通过局部变量实现
- 通过拷贝数据到自己的空间,自己修改自己拷贝的数据,这样不会影响其他线程,一般通过thread.local实现
- 控制共享变量的访问方式,同一时刻只能有一个线程修改,一般通过加锁实现
还有一些其他方法保证线程安全,不同的场景,保证线程安全的方法也不同,需要合理采用。下面用代码说明一下
首先,我们不加锁,有两个线程共同操作一个共享变量
# 没有使用锁的情况 from threading import Thread n = 0 def add(): global n for i in range(1000000): n += 1 def sub(): global n for i in range(1000000): n -= 1 t1 = Thread(target=add) t2 = Thread(target=sub) t1.start() t2.start() t1.join() t2.join() print(n)
输出的结果,发现不是我们想象中的0,这就是因为两个线程运行期间,一个线程把 对n的操作执行到一半时候,去休息了,回来继续执行剩下一半的过程中,另一个线程修改了n,这样就造成了线程不安全的问题,最后的结果不一致。
加锁后的代码
from threading import Thread, Lock n = 0 lock = Lock() def add(lock): global n for i in range(1000000): lock.acquire() n += 1 lock.release() def sub(lock): global n for i in range(1000000): lock.acquire() n -= 1 lock.release() t1 = Thread(target=add, args=(lock,)) t2 = Thread(target=sub, args=(lock,)) t1.start() t2.start() t1.join() t2.join() print(n)
这次输出的结果都是0了,我们在整个修改共享变量的过程中,加了锁,在锁未释放的时候,其他线程是运行不了的
四、线程间通信
上面我们知道存在线程安全的问题,所以通信的过程中,我们要考虑线程安全的问题
线程间通信最常用的是队列,python提供了一个线程安全的队列,from queue import Queue,在多个线程之间操作队列里的元素都是安全的
还有更加复杂的线程通信机制,比如条件变量,信号量等,python也提供了相应的模块
五、线程池
有时候,我们有很多条数据,我们希望同一时刻总共有5个线程来处理这一批数据,一个线程结束后,再启动另一个线程,总数不能超过5个,这时候线程池就可以很好的解决我们的问题了
from concurrent.futures.thread import ThreadPoolExecutor from concurrent.futures import wait import time pools = ThreadPoolExecutor(5) data = list(range(20)) res = [] def fun(n): time.sleep(2) print(n) return n for n in data: res.append(pools.submit(fun, n))
# 等待所有线程执行完毕 wait(res)
线程池还给我们提供了更多的功能,我们可以获取线程返回的结果
from concurrent.futures.thread import ThreadPoolExecutor from concurrent.futures import as_completed import time pools = ThreadPoolExecutor(5) data = list(range(20)) res = [] def fun(n): time.sleep(2) return n for n in data: res.append(pools.submit(fun, n)) # 获取线程结束后的结果 for r in as_completed(res): print(r.result())
六、多进程编程
关于python多进程,在大多数业务开发中,用的比较少,我只给出一个简单的例子(实际上进程在运行的时候比这个负责,是会拷贝一份父进程的信息的)
from multiprocessing import Process import time def fun(n): # do something time.sleep(n) print(n) if __name__ == "__main__": p1 = Process(target=fun, args=(1,)) p2 = Process(target=fun, args=(2,)) p1.start() p2.start() p1.join() p2.join()
七、总结
并发编程是我们提高程序性能的常用手段,一般来说就是多线程,多进程,协程,并发编程的时候,我们还有许多问题需要考虑,线程安全,通信等等。
原文链接:https://www.cnblogs.com/time-read/p/11119032.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:Python-03-流程控制
下一篇:Python中的逻辑运算符
- python3基础之“术语表(2)” 2019-08-13
- python3 之 字符串编码小结(Unicode、utf-8、gbk、gb2312等 2019-08-13
- Python3安装impala 2019-08-13
- 小白如何入门 Python 爬虫? 2019-08-13
- python_字符串方法 2019-08-13
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash