线程
内容概要
一、线程与进程的区别
二、开启线程的方式
三、线程的相关方法
四、守护线程
五、利用线程实现TCP并发
六、GIF全局解释器锁
1、线程与进程的区别
-线程:是计划单位,开设时消耗很少资源,同一进程下多个线程的数据是共同的。(想象成流水线)
-进程:是资源单位,开设时要申请内存空间,拷贝代码,多个进程的数据是隔离的。(想象成车间)
-cpu:是真正的执行单位。(想象成工人)
ps:一个车间存放有原材料,并且可以设置有很多条流水线;每条流水线都需要车间提供的原材料以及工人的操作才能生产出产品
多进程和多线程在什么情况下使用(仅讨论多核情况)
-计算密集型
from multiprocessing import Process from threading import Thread import os import time def work(): num = 0 for i in range(100000000): num *= i if __name__ == '__main__': print(os.cpu_count()) # 获取cpu核心数,我的是12核 l = [] start_time = time.time() # p = Process(target=work,) # 10.138535022735596 # p.start() # l.append(p) for i in range(12): t = Thread(target=work,) # 55.375882148742676 t.start() l.append(t) for k in l: k.join() end_time = time.time() print(end_time-start_time)
io密集型
from multiprocessing import Process from threading import Thread import os import time def work(): time.sleep(2) if __name__ == '__main__': print(os.cpu_count()) # 获取cpu核心数,我的是12核 l = [] start_time = time.time() for i in range(400): # p = Process(target=work,) # 5.743039846420288 # p.start() # l.append(p) t = Thread(target=work,) # 2.0436341762542725 t.start() l.append(t) for k in l: k.join() end_time = time.time() print(end_time-start_time)
结论:当执行程序是计算密集型时,多进程优势大;当执行程序时io密集型时,多线程优势大
原因分析:
2、开启线程的两种方式,与开启进程类似
-方式一:
from threading import Thread import time def task(): print('线程执行中') time.sleep(1) print('线程执行完毕') if __name__ == '__main__': t = Thread(target=task,) t.start() print('主')
开启线程不需要在 if __name__ == '__main__' :下执行,但为了与进程保持一致性,习惯写上 if __name__ == '__main__' :
-方式二(利用类的继承与类下的run方法)
from threading import Thread class MyThread(Thread): def __init__(self,name): Thread.__init__(self) self.name = name def run(self): print(self.name) t = MyThread('egon') t.start()
3、线程的相关方法
-join方法:等待子线程都执行完毕之后,主线程才接着执行
-current_thread().name方法:获取线程名
-active_count方法:检测活跃的线程数
from threading import Thread,current_thread,active_count import time def task(): print('线程执行中') print(active_count()) # 打印活跃线程数 print(current_thread().name) # 打印子线程名 time.sleep(1) print('线程执行完毕') if __name__ == '__main__': t = Thread(target=task,) t.start() t.join() print('主',end='') # 打印主线程名 print(current_thread().name) # 执行结果: # 线程执行中 # 2 # Thread-1 # 线程执行完毕 # 主MainThread
4、守护线程:
-当主线程执行完它的代码时,主线程不会立刻结束,而是会等待其它所有非守护线程的线程执行结束之后再结束
from threading import Thread import time def task(): print('线程执行中') time.sleep(1) print('线程执行完毕') if __name__ == '__main__': t = Thread(target=task,) t.daemon = 1 # 将线程对象t设置为守护线程 t.start() print('主')
5、利用线程实现TCP链接并发
-服务端
from threading import Thread import socket def task(conn): while 1: try: data = conn.recv(1024) if data is None: break print(data.decode('utf-8')) conn.send(data.upper()) except ConnectionResetError as e: print(e) conn.close() break server = socket.socket() server.bind(('127.0.0.1', 6666)) server.listen(5) while 1: print('没有卡在这里') conn, addr = server.accept() print('卡在这里了') t = Thread(target=task,args=(conn,)) t.start()
-客户端
import socket client = socket.socket() client.connect(('127.0.0.1',6666)) while 1: client.send(b'hello world') data = client.recv(1024) print(data.decode('utf-8'))
6、GIF全局解释器锁
-GIF不是python这门语言的特点,而是Cpython这个解释器的特点
-GIF本质也是一把互斥锁,它是解释器级别数据的锁
-GIF的出现是由于python的垃圾回收机制下不是线程安全的,因为同一个进程下的多个线程的数据是共享的,多个线程同时对同一数据进程修改,可能导致数据错乱
垃圾回收机制
-引用计数
-标记清楚
-分代回收
-GIF的出现导致多线程无法并行,只能并发,即无法使用多核优势
注意:当拿到GIF锁权限的线程执行过程中遇到IO阻塞时或者时间片到时,GIF锁会被释放,cpu执行权限会强制剥夺。其他线程此时可以获取GIF锁权限,被cpu执行
详情引用:https://www.cnblogs.com/xiaoyuanqujing/protected/articles/11715730.html


本文来自博客园,作者:口乞厂几,转载请注明原文链接:https://www.cnblogs.com/laijianwei/p/14435282.html

浙公网安备 33010602011771号