多线程
概念
线程是进程中一个“单一的连续控制流程”/执行路径
- 线程被称为轻量级进程
- 一个进程可以拥有多个并行的线程
- 一个进程中的线程共享相同的内存单元/内存地址空间(可以访问相同的变量和对象),而且他们从同一个堆中分配对象(通信,数据交换,同步操作)
- 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这使得通信更简便并且信息传递的速度更快。
线程几种状态
多线程程序的执行顺序是不确定的,当执行到sleep时,线程将被阻塞(bloceed),到sleep结束后,线程进入就绪(runable)状态等待调度。而线程调度将自行选择一个线程执行。
![ff81bb204b5b11c3a90ff3f57bc24b88.png](https://img-blog.csdnimg.cn/img_convert/ff81bb204b5b11c3a90ff3f57bc24b88.png)
在这里插入图片描述
多线程创建方式
py中的thread模块是比较底层的模块,而threading模块是对thread模块做了一些包装,使之可以更方便的被调用。
threading模块
- threading.enumrate(): 查看当前进程线程数量
threading.Thread类
- 初始化参数:和多进程Process的初始化方法作用一致
- is_alive():是否存活
- join(): 等待创建的线程执行完
- run():线程创建后需要执行的方法,可以被重写
- start():启动线程
创建线程方式一:传入方法运行
import os,time,random,threadingdef say(name): print('%sSorry'%name)if __name__=='__main__': t=threading.Thread(target=say,args=('MM',)) t.start() t.join()'''MMSorry'''
创建线程方式二:子类化
import os,time,random,threadingclass Thread_Test(threading.Thread): def __init__(self,name): #注意:初始化要放在参数赋值前面 threading.Thread.__init__(self) self.name=name def run(self): print('当前进程名%s'%(self.name,))if __name__=='__main__': t=Thread_Test('MM') t.start() t.join()'''当前进程名MM'''
线程共享全局变量问题
- 优点:在一个进程内的所有线程共享全局变量,能够在不适用其它方式的前提下完成多线程之间的数据共享
- 缺点:线程对全局变量的随意改变可能造成多线程对全局变量的混乱。
共享全局变量编程:使用global关键字
当然也可以使用给方法传参的方式
import os,time,random,threadingnum=100def fun1(): #声明num为全局变量 global num for i in range(3): num+=1 print(str(num),'>>>fun1')def fun2(): global num print(num,'>>>fun2')t1=threading.Thread(target=fun1)t1.start()time.sleep(1)t2=threading.Thread(target=fun2)t2.start()'''通过结果可以看出,两个线程共享num变量101 >>>fun1102 >>>fun1103 >>>fun1103 >>>fun2'''
ThreadLocal变量
- 一个ThreadLocal变量虽然是全局变量,但是每个线程都只能读写自己线程的独立副本。这样就解决了参数在一个线程中各个参数之间的传递问题。
- ThreadLocal最常用的操作就是为每一个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用函数都可以方便的访问资源。
import os,time,random,threadingdef fun1(name): local.name=name fun2()def fun2(): name=local.name print(name)#创建全局的ThreadLocal对象local=threading.local()for i in range(5): threading.Thread(target=fun1,args=(i,)).start()
线程同步问题(加锁)
概念
- 多个线程几乎在同时修改某一共享数据时,需要进行同步控制
- 线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制就是引入互斥锁
- 互斥锁保证每次只有一个线程能够进程写操作,从而保证数据的准确性
- threading模块定义了lock类来进行锁操作
lock类
- Lock():创建锁
- acquire([blocking]): 进入同步状态,如果blocking为True,则当前线程会阻塞,直到获取锁,False则不会阻塞。默认为True。
- release():释放锁
同步编程
import os,time,random,threadingnum=100def fun1(): #声明num为全局变量 global num #获取锁 lock.acquire() try: for i in range(100): num += 1 print(str(num), '>>>fun1') finally: #释放锁 lock.release()def fun2(): global num lock.acquire() try: for i in range(100): num += 1 print(str(num), '>>>fun2') finally: lock.release()lock=threading.Lock()t1=threading.Thread(target=fun1)t1.start()time.sleep(1)t2=threading.Thread(target=fun2)t2.start()
死锁问题
线程间共享多个资源时,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会进入死锁状态。
import os,time,random,threadingnum=100def fun1(): #声明num为全局变量 global num #获取锁 lock1.acquire() try: print('>>>fun1') time.sleep(1) lock2.acquire() for i in range(100): num += 1 print(str(num), '>>>fun1') lock2.release() finally: #释放锁 lock1.release()def fun2(): global num lock2.acquire() print('>>>fun2') time.sleep(1) try: lock1.acquire() for i in range(100): num += 1 print(str(num), '>>>fun2') lock1.release() finally: lock2.release()lock1=threading.Lock()lock2=threading.Lock()t1=threading.Thread(target=fun1)t2=threading.Thread(target=fun2)t1.start()t2.start()'''>>>fun1>>>fun2'''
线程间消息队列
py的Queue模块提供了同步的,线程安全的队列类
- FIFO(先进先出):Queue
- LIFO(先进后出): LifoQueue ,栈模式
- 优先级队列:PriorityQueue
这些队列都是实现了锁原语(原子操作),可以在多线程中直接使用。
生产者消费者
from queue import Queue,LifoQueue,PriorityQueueimport os,time,random,threadingclass Producer(threading.Thread): global queue def run(self): count=0 while True: if queue.qsize()<1000: count+=1 msg='生产者'+str(count) queue.put(msg) print(msg)class Consumer(threading.Thread): def run(self): global queue while True: if queue.qsize()>0: msg=self.name+'消费了'+queue.get() print(msg)#queue=Queue()queue=PriorityQueue()#queue=LifoQueue()for i in range(5): Producer().start()for i in range(5): Consumer().start()