线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁...
线程(from threading import Thread):CPU调度的最小单位
线程的两种创建方式:方式一:
1 from threading import Thread 2 def f1(i): 3 print(i) 4 if __name__ == '__main__': 5 for i in range(10): 6 t = Thread(target=f1,args=(i,)) 7 t.start() 8 print('主线程')
方式二:
1 from threading import Thread 2 class MyThread(Thread): 3 def __init__(self, i): 4 super().__init__() 5 self.i = i 6 7 def run(self): 8 print('%s哈哈' % self.i) 9 10 if __name__ == '__main__': 11 for i in range(10): 12 t = MyThread(i) 13 t.start() 14 print('主线程')
线程之间数据共享的验证:
1 import time 2 from threading import Thread 3 num = 100 4 def f1(): 5 global num 6 num = 33 7 time.sleep(2) 8 print('num>>>',num) 9 if __name__ == '__main__': 10 t = Thread(target=f1,) 11 t.start() 12 print('主线程的num',num)
多线程跟多进程的效率对比:
结论:当遇到io阻塞情况,多线程比多进程效率高,当遇到大量计算情况,多进程比多线程效率高
1 import time 2 from threading import Thread 3 from multiprocessing import Process 4 def f1(): 5 # time.sleep(1) 6 n = 10 7 for i in range(10000000): 8 n += i 9 if __name__ == '__main__': 10 t_s_time = time.time() 11 t_list = [] 12 for i in range(10): 13 t = Thread(target=f1,) 14 t.start() 15 t_list.append(t) 16 [tt.join() for tt in t_list] 17 t_e_time = time.time() 18 t_dif_time = t_e_time - t_s_time 19 20 p_s_time = time.time() 21 p_list = [] 22 for i in range(10): 23 p = Process(target=f1,) 24 p.start() 25 p_list.append(p) 26 [pp.join() for pp in p_list] 27 p_e_time = time.time() 28 p_dif_time = p_e_time - p_s_time 29 print('线程的时间:',t_dif_time) 30 print('进程的时间:',p_dif_time)
线程锁(互斥锁):
作用:牺牲了效率,保证了数据安全
1 import time 2 from threading import Thread,Lock 3 num = 100 4 def f1(t_lock): 5 t_lock.acquire() 6 global num 7 tme = num 8 tme -= 1 9 num = tme 10 time.sleep(1) 11 print('子线程的num',num) 12 t_lock.release() 13 if __name__ == '__main__': 14 t_lock = Lock() 15 t_list = [] 16 for i in range(10): 17 t = Thread(target=f1,args=(t_lock,)) 18 t.start() 19 t_list.append(t) 20 [tt.join() for tt in t_list] 21 print('主线程的num',num)
死锁现象:出现在锁嵌套的时候,双方互相抢对方已经拿到的锁,导致双方互相等待,程序卡住(重点)
死锁模拟:
1 import time 2 from threading import Thread,Lock 3 def f1(t_lockA,t_lockB): 4 t_lockA.acquire() 5 print('f1抢到了A锁') 6 time.sleep(1) 7 t_lockB.acquire() 8 print('f1抢到了B锁') 9 t_lockB.release() 10 t_lockA.release() 11 def f2(t_lockA,t_lockB): 12 t_lockB.acquire() 13 print('f2抢到了B锁') 14 time.sleep(1) 15 t_lockA.acquire() 16 print('f2抢到了A锁') 17 t_lockA.release() 18 t_lockB.release() 19 if __name__ == '__main__': 20 t_lockA = Lock() 21 t_lockB = Lock() 22 t1 = Thread(target=f1,args=(t_lockA,t_lockB)) 23 t2 = Thread(target=f2,args=(t_lockA,t_lockB)) 24 t1.start() 25 t2.start()
递归锁:最常用的线程锁,解决了死锁现象
Rlock 首先本身就是个互斥锁,维护了一个计数器,每次acquire就+1,release就-1,当计数器为0的时候,大家才能抢这个锁
1 import time 2 from threading import Thread,RLock 3 def f1(lokA,lokB): 4 lokA.acquire() 5 print('f1抢到了A锁') 6 time.sleep(1) 7 lokB.acquire() 8 print('f1抢到B锁了') 9 lokB.release() 10 lokA.release() 11 def f2(lokA,lokB): 12 lokB.acquire() 13 print('f2抢到了B锁') 14 time.sleep(1) 15 lokA.acquire() 16 print('f2抢到了A锁') 17 lokA.release() 18 lokB.release() 19 if __name__ == '__main__': 20 lokA = lokB = RLock() #递归锁,维护一个计数器,acquire一次就加1,release就减1 21 t1 = Thread(target=f1,args=(lokA,lokB)) 22 t2 = Thread(target=f2,args=(lokA,lokB)) 23 t1.start() 24 t2.start()
多线程守护跟多进程守护的区别:
区别: 守护进程:主进程代码执行运行结束,守护进程随之结束 守护线程:守护线程会等待所有非守护线程运行结束才结束
代码示例:
1 import time 2 from threading import Thread 3 from multiprocessing import Process 4 def f1(): 5 time.sleep(2) 6 print('一号') 7 def f2(): 8 time.sleep(3) 9 print('二号') 10 if __name__ == '__main__': 11 t1 = Thread(target=f1,) 12 t2 = Thread(target=f2,) 13 t1.daemon = True 14 t2.daemon = True #守护线程:守护线程会等待所有非守护线程运行结束才结束 15 t1.start() 16 t2.start() 17 print('线程结束') 18 p1 = Process(target=f1,) 19 p2 = Process(target=f2,) 20 p1.daemon = True 21 p2.daemon = True #守护进程:主进程代码执行运行结束,守护进程随之结束 22 p1.start() 23 p2.start() 24 print('进程结束')
GIL锁 : cpython解释器上的一把互斥锁 首先需要明确的一点是GIL
并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成 可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。 像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL
归结为Python语言的缺陷。 所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL
GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
可以肯定的一点是:保护不同的数据的安全,就应该加不同的锁。
要想了解GIL,首先确定一点:每次执行python程序,都会产生一个独立的进程。例如python test.py,python aaa.py,python bbb.py会产生3个不同的python进程
1 ''' 2 #验证python test.py只会产生一个进程 3 #test.py内容 4 import os,time 5 print(os.getpid()) 6 time.sleep(1000) 7 ''' 8 python3 test.py 9 #在windows下 10 tasklist |findstr python 11 #在linux下 12 ps aux |grep python
如果多个线程的target=work,那么执行流程是
多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码去执行
解释器的代码是所有线程共享的,所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行x=100的同时,而垃圾回收执行的是回收100的操作,解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码
GIL保护的是解释器级的数据,保护用户自己的数据则需要自己加锁处理,如下图
结论:
对计算来说,cpu越多越好,但是对于I/O来说,再多的cpu也没用
当然对运行一个程序来说,随着cpu的增多执行效率肯定会有所提高(不管提高幅度多大,总会有所提高),这是因为一个程序基本上不会是纯计算或者纯I/O,所以我们只能相对的去看一个程序到底是计算密集型还是I/O密集型,从而进一步分析python的多线程到底有无用武之地
#分析: 我们有四个任务需要处理,处理方式肯定是要玩出并发的效果,解决方案可以是: 方案一:开启四个进程 方案二:一个进程下,开启四个线程#单核情况下,分析结果: 如果四个任务是计算密集型,没有多核来并行计算,方案一徒增了创建进程的开销,方案二胜如果四个任务是I/O密集型,方案一创建进程的开销大,且进程的切换速度远不如线程,方案二胜#多核情况下,分析结果: 如果四个任务是计算密集型,多核意味着并行计算,在python中一个进程中同一时刻只有一个线程执行用不上多核,方案一胜如果四个任务是I/O密集型,再多的核也解决不了I/O问题,方案二胜#结论:现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。
计算密集型:多进程效率高
1 from multiprocessing import Process 2 from threading import Thread 3 import os,time 4 def work(): 5 res=0 6 for i in range(100000000): 7 res*=i 8 9 10 if __name__ == '__main__': 11 l=[] 12 print(os.cpu_count()) #本机为4核 13 start=time.time() 14 for i in range(4): 15 p=Process(target=work) #耗时5s多 16 p=Thread(target=work) #耗时18s多 17 l.append(p) 18 p.start() 19 for p in l: 20 p.join() 21 stop=time.time() 22 print('run time is %s' %(stop-start))
I/O密集型:多线程效率高
1 from multiprocessing import Process 2 from threading import Thread 3 import threading 4 import os,time 5 def work(): 6 time.sleep(2) 7 print('===>') 8 9 if __name__ == '__main__': 10 l=[] 11 print(os.cpu_count()) #本机为4核 12 start=time.time() 13 for i in range(400): 14 # p=Process(target=work) #耗时12s多,大部分时间耗费在创建进程上 15 p=Thread(target=work) #耗时2s多 16 l.append(p) 17 p.start() 18 for p in l: 19 p.join() 20 stop=time.time() 21 print('run time is %s' %(stop-start))
应用:
多线程用于IO密集型,如socket,爬虫,web
多进程用于计算密集型,如金融分析
写一个简易socket多人聊天:
1 import socket 2 from threading import Thread 3 class MySocket: 4 def __init__(self,server_addr): 5 self.server_addr = server_addr 6 self.socket = socket.socket() 7 def serve_forever(self): 8 self.socket.bind(self.server_addr) 9 self.socket.listen(5) 10 self.build_connect() 11 def build_connect(self): #建立连接 12 while 1: 13 conn,addr = self.socket.accept() 14 t = Thread(target=self.handle,args=(conn,)) 15 t.start() 16 def handle(self,conn): 17 while 1: 18 from_client_msg = conn.recv(1024).decode('utf-8') 19 print('来自客户的消息:',from_client_msg) 20 to_client_msg = input('服务端说:') 21 conn.send(to_client_msg.encode('utf-8')) 22 if __name__ == '__main__': 23 ip_port = ('127.0.0.1',8013) 24 server = MySocket(ip_port) 25 server.serve_forever()
1 import socket 2 client = socket.socket() 3 client.connect(('127.0.0.1',8013)) 4 while 1: 5 to_server_msg = input('客户端说:') 6 client.send(to_server_msg.encode('utf-8')) 7 from_server_msg = client.recv(1024).decode('utf-8') 8 print('来自服务端的消息:',from_server_msg)
转载于:https://www.cnblogs.com/Godisgirl/p/10257681.html
线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁...相关推荐
- 036-2018-1028 线程 效率对比 数据共享 同步锁死锁递归锁 守护线程 信号量
笔记 昨日内容回顾: 队列:Queue 管道 : Pipe ,recv消息的时候 OSError,EOFError,数据不安全的 Manager : 共享数据 , 数据不安全 , 加锁 进程池 : P ...
- Java高并发编程:多个线程之间共享数据的方式探讨
内容摘要 多个线程之间共享数据,按照每个线程执行代码是否相同,我们可以采取不同的处理方式,这里通过简单的卖票示例说明了当每个线程执行相同代码的情况,对于多个线程执行不同代码的情况,处理方式比较灵活,这 ...
- 如何在两个线程之间共享数据
两个线程之间共享数据的方式 Java 里面进行多线程通信的主要方式就是共享内存的方式,共享内存主要的关注点有两个:可见 性和有序性原子性.Java 内存模型(JMM)解决了可见性和有序性的问题,而锁解 ...
- python主线程有两个子线程、创建两个主函数_Python多任务之线程
1.1. 线程 threading.Thread(target = 函数名) 线程的运行是没有先后顺序的 主线程死了,子线程必死.子线程结束后,子线程会给子线程收尸. 当调用Thread的时候,不会创 ...
- docker 容器共享数据_如何在Docker容器之间共享数据
docker 容器共享数据 介绍 (Introduction) Docker is a popular containerization tool used to provide software a ...
- 使用内存映射文件在进程之间共享数据
数据共享方法是通过让两个或多个进程映射同一个文件映射对象的视图来实现的,这意味着它们将共享物理存储器的同一个页面.因此,当一个进程将数据写入一个共享文件映射对象的视图时,其他进程可以立即看到它们视图中 ...
- 在AngularJS控制器之间共享数据
本文翻译自:Share data between AngularJS controllers I'm trying to share data across controllers. 我正在尝试跨控制 ...
- 用内存映射在多个应用程序之间共享数据
文件的内存映射的主要用途有两个方面,第一是用来在多个进程之间共享数据,第二是直接用内存映射文件来访问磁盘上的数据文件,无需要进行文件的I/O操作.进程间共享数据有很多种方法,今天,我主要讲的是内存映射 ...
- Android应用程序组件Content Provider在应用程序之间共享数据的原理分析(1)
在Android系统中,不同的应用程序是不能直接读写对方的数据文件的,如果它们想共享数据的话,只能通过Content Provider组件来实现.那么,Content Provide ...
最新文章
- leetcode1017
- 北斗产业发展现状与前景预测研究
- Oracle初始化参数含义
- NYOJ 110 剑客决斗
- Web安全1沙箱隔离
- python字典随时添加元素和值
- MySQL 使用 OR 条件导致索引失效
- Web- HTML网页颜色大全
- web性能优化(理论)
- VMware SD-WAN 修复6个漏洞,可关闭整个企业网络
- Jenkins部署Web项目到远程tomcat(通过jenkins插件)
- 普通人学python有意义吗-风变编程:普通人学Python有意义吗?
- python中print说法正确的是_python中的print()输出
- 批判性思维-真理连贯论
- AI 写代码来了 - github 的 AI 写代码插件 copilot 发布
- mysql查询字段最大的一条数据类型_SQL查询一个表中类别字段中Max()最大值对应的记录...
- 读中国通史的简注(周朝开始)
- 仿QQ和飞秋并支持语音视频白板屏幕共享的即时聊天软件
- 《算法笔记》Codeup练习 5.1小节 简单数学问题
- 人证核验一体如何验证考生身份对比