1.线程的概念:
1. 什么是线程: 进程和线程都是虚拟单位,都是用来帮助我木门形象的描述某种事物
进程 : 资源单位 线程:执行单位
每一个进程都自带一个线程,线程才是真正的执行单位,进程只是在线程运行过程中提供代码运行所需要的资源
2. 为什么要有线程:
开进程 1 申请内存空间 耗资源, 拷贝代码, 耗资源
开线程 一个进程内可以起多个线程,并且线程与线程之间的数据是共享的
开启线程的开销要远远小于开启进程的开销
2.Threading.Thread 创建线程模块
Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
3.代码的实现
1.创建线程的两种方式
from threading import Threadimport timedef func(name): print(f'{name}线程正在创建') time.sleep(2) print(f'{name}线程结束')# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内if __name__ == '__main__': t = Thread(target=func,args=('小明',)) t.start() print('主')
from threading import Threadimport timeclass MyThread(Thread): def __init__(self,name): super().__init__() self.name = name def run(self): print(f'{self.name}线程正在创建') time.sleep(2) print(f'{self.name}线程结束')if __name__ == '__main__': t = MyThread('小明') t.start() print('主')
2.线程对象及其他方法:
from threading import Thread,current_thread,active_countimport timeimport osdef task(name,i): print('%s is running'%name) # print('子current_thread:',current_thread().name) # print('子',os.getpid()) time.sleep(i) print('%s is over'%name)# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内t = Thread(target=task,args=('egon',1))t1 = Thread(target=task,args=('jason',2))t.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程t1.start() # 告诉操作系统开辟一个线t1.join() # 主线程等待子线程运行完毕print('当前正在活跃的线程数',active_count())# 小的代码执行完 线程就已经开启了print('主')# print('主current_thread:',current_thread().name) # 线程名字# print('主',os.getpid())
3. 守护线程:与进程方法相同,但需要注意的是:主线程的结束也就以为着进程的结束,主线程必须等待其他非守护线程的结束才能结束,也就是意味着子线程在运行的时候需要使用进程中的资源,而主线程一旦结束了资源也就销毁了
from threading import Thread,current_threadimport timedef task(i): print(current_thread().name) time.sleep(i) print('GG')t = Thread(target=task,args=(1,))t.daemon = Truet.start()print('主') '''Thread-1主 因为t线程为守护进程,主线程运行结束t线程也跟着结束'''
4. 同一进程中线程之间的数据是共享的
from threading import Threadmoney = 666def task(): global money money = 999t = Thread(target=task)t.start()t.join()print(money) # 999 同一进程中线程间的数据是共享的
5. 线程中的互斥锁与进程中的使用方法一致
from threading import Thread,Lockimport timen = 100def task(mutex): global n mutex.acquire() tmp = n time.sleep(0.1) n = tmp - 1 mutex.release()t_list = []mutex = Lock()for i in range(100): t = Thread(target=task,args=(mutex,)) # 创建100个线程 t.start() t_list.append(t) # 将线程对象添加到列表中for t in t_list: # 遍历线程对象列表 t.join() # 等待所有线程程运行结束再执行主线程print(n) # 0
6. 用线程实现sockte服务端的并发:
服务端:
import socketfrom threading import Threaddef talk(conn): while 1: try: ret = conn.recv(1024) if len(ret) == 0: break print(ret) conn.send(ret.upper()) except ConnectionResetError: break conn.close()sk = socket.socket()sk.bind(('127.0.0.1',8080))sk.listen()while 1: conn,addr = sk.accept() t = Thread(target=talk,args=(conn,)) t.start()
客户端:
import socketsk = socket.socket()sk.connect(('127.0.0.1',8080))while 1: sk.send(b'hello') ret = sk.recv(1024) print(ret)
7: GIL全局解释器锁:
"""In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiplenative threads from executing Python bytecodes at once. This lock is necessary mainlybecause CPython’s memory management is not thread-safe."""
ps:python解释器有很多种 最常见的就是Cpython解释器
GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全 他是用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)GIL的存在是因为CPython解释器的内存管理不是线程安全的,也就是说同一个进程中的多个线程再同一时间只有一个线程被cpu执行,GIL全局解释器锁只是针对线程的,进入IO自动释放总结.在执行多个计算密集型的任务时.在计算机单核的情况下开线程最好,在多核情况下开进程会效率更高
.在执行多个IO密集型的任务时.开线程时最好的
8:死锁与递归锁:当一个进程或线程中同时出现多把锁容易造成死锁显现,程序卡死
递归锁: Rlock,可以多次acquire与release 当一个线程或进程中所有acquire都被release,其他的线程才能获得资源,注意使用递归锁时是链式赋值
科学家吃面示例:
import timefrom threading import Thread,RLocklock1 = lock2 = RLock()def eat1(name): lock2.acquire() print('%s 抢到了面条'%name) lock1.acquire() print('%s 抢到了叉子'%name) print('%s 吃面'%name) lock1.release() lock2.release()def eat2(name): lock1.acquire() print('%s 抢到了叉子' % name) time.sleep(1) lock2.acquire() print('%s 抢到了面条' % name) print('%s 吃面' % name) lock2.release() lock1.release()for name in ['小明','小黄','小亮']: t1 = Thread(target=eat1,args=(name,)) t2 = Thread(target=eat2,args=(name,)) t1.start() t2.start()
9 信号量: Semaphore: 用锁的原理实现的,内置了一个计数器,在同一时间只能有指定数量的进程或线程执行某一段代码,如果把互斥锁比作一个车位的话,那么信号量相当于多个车位
from threading import Thread,Semaphore # 线程使用信号量导入方法# from multiprocessing import Process,Semaphore # 进程使用信号量导入方法import timeimport randomsm = Semaphore(5) # 5个车位def func(name): sm.acquire() # 占车位 print(f'{name}占了一个车位') time.sleep(random.randint(1,2)) sm.release() # 让出车位 print(f'{name}让了一个车位')for i in range(20): t = Thread(target=func,args=(i,)) t.start()'''同时占用车位最多不会超过5个,一个让出去另一个才会占用'''
10:事件:event类似于join 不过事件是在处理两个子进程或子线程之间互相等待的过程
常用方法:
from multiprocessing import Evente = Event()e.wait() # 阻塞态,状态为True不堵塞,为False 堵塞e.is_set() # 查看状态 e.set() # False ---> Truee.clear() # True ---> False
红绿灯示例:
from threading import Event,Threadimport timee = Event()def light(): print('红灯亮了') time.sleep(3) e.set() print('绿灯亮了')def car(name): print(f'{name}正在等红灯') e.wait() print(f'{name}可以行驶了')l = Thread(target=light)l.start()for i in range(10): c = Thread(target=car,args=(i,)) c.start()
11. 线程队列:
1: queue 队列
import queueq = queue.Queue()q.put(1)q.put(2)print(q.get()) # 1print(q.get()) # 2
2: Lifoqueue 堆栈
import queueq = queue.LifoQueue() # 堆栈q.put(1)q.put(2)print(q.get()) # 2print(q.get()) # 1
3: PriorityQueue 优先级队列
import queueq = queue.PriorityQueue() # 堆栈q.put((1,'a')) # 放一个元组,第一个元素是优先级为int型,数字越小优先级越高q.put((0,'b')) q.put((-1,'c'))q.put((1,'d')) # 同一优先级,按照内容对应的ASCII码表来确认优先级print(q.get()) # (-1, 'c')print(q.get()) # (0, 'b')print(q.get()) # (1, 'a')print(q.get()) # (1, 'd')