商通达,排队系统,真假茱丽叶
python多线程原理:python的多线程实际是一个假的多线程(多个线程在1核cpu上运行,进行快速的切换导致错觉为同时执行)
代码
import threading def func(arg): print(arg) th = threading.thread(target=func) th.start() print('end')
一个应用程序:软件
python多线程情况下:
python多进程情况下:
以后写python时:
注意:进程是为了提供环境让线程工作
gil锁,全局解释器锁。用于限制一个进程中同一个时刻只有一个线程被cpu调度
扩展:默认gil锁在执行100个cpu指令后(过期时间)。
import threading def func(arg): print(arg) th = threading.thread(target=func) th.start() print('end')
import time import threading def func(arg): time.sleep(arg) print(arg) t1 = threading.thread(target=func,args=(3,)) t1.start() t2 = threading.thread(target=func,args=(9,)) t2.start() print('end')
import time import threading def func(arg): time.sleep(3) print(arg) print('开始执行t1') t1 = threading.thread(target=func,args=(3,)) t1.start() # 无参:让主线程等待,等到子线程t1执行完毕后,才能往下执行 # 有参:让主线程在这里最多等待n秒,无论执行完毕与否,会继续往下走 t1.join(2) print('开始执行t2') t2 = threading.thread(target=func,args=(9,)) t2.start() t2.join() # 让主线程等待,等到子线程t1执行完毕后,才能往下执行 print('end')
import threading def func(arg): # 获取当前执行该函数的线程的对象 t = threading.current_thread() # 根据当前线程对象,获取当前线程名称 name = t.getname() print(name,arg) t1 = threading.thread(target=func,args=(3,)) t1.setname('mhy') t1.start() t2 = threading.thread(target=func,args=(9,)) t2.setname('zz') t2.start() print('end')
# 先打印3还是end? import threading def func(arg): print(arg) t1 = threading.thread(target=func,args=(3,)) # start是开始线程嘛?不是 # start是告诉cpu,我已准备就绪,你可以调度我了。 t1.start() print('end')
import threading class mythread(threading.thread): def run(self): print(123,self._args,self._kwargs) t1 = mythread(args=(11,)) t1.start() t2 = mythread(args=(12,)) t2.start()
import threading def func(lis,num): et = [i+num for i in lis] print(et) t1 = threading.thread(target=func,args=([11,22,33],1)) t1.start() t2 = threading.thread(target=func,args=([44,55,66],100)) t2.start()
import threading import time #定义类继承thread线程 class codingthread(threading.thread): def run(self): for x in range(3): print('正在写代码%s' % threading.current_thread()) time.sleep(1) class drawingthread(threading.thread): def run(self): for x in range(3): print('正在画图%s' % threading.current_thread()) time.sleep(1) def main(): t1 = codingthread() t2 = drawingthread() t1.start() t2.start() if __name__ == '__main__': main()
python自带的解释器是cpython。cpython解释器的多线程实际上是一个假的多线程(在多核cpu中,只能利用一核,不能利用多核)。同一时刻只有一个线程在执行,为了保证同一时刻只有一个线程在执行,在cpython解释器中有一个东西叫做gil(global intepreter lock),叫做全局解释器锁。这个解释器锁是有必要的。因为cpython解释器的内存管理不是线程安全的。当然除了cpython解释器,还有其他的解释器,有些解释器是没有gil锁的,见下面:
gil虽然是一个假的多线程。但是在处理一些io操作(比如文件读写和网络请求)还是可以在很大程度上提高效率的。在io操作上建议使用多线程提高效率。在一些cpu计算操作上不建议使用多线程,而建议使用多进程。
import time import threading lit = [] def func(arg): lit.append(arg) time.sleep(0.04) m = lit[-1] print(arg,m) # m和arg应该是一个值 for num in range(10): t1 = threading.thread(target=func,args=(num,)) t1.start()
多线程同时执行,会导致数据同时执行,获取数据不符合结果。
线程先获得执行权时,将会将执行过程锁起来,其他线程不能使用,必须要等该线程执行完,其他线程才可获取执行权,执行线程,将解决数据不统一的方法
import time import threading lit = [] lock = threading.lock() # 创建一个锁 def func(arg): lock.acquire() # 将该行代码以下代码锁起来,直到遇到release释放 lit.append(arg) time.sleep(0.04) m = lit[-1] print(arg,m) lock.release() # 释放锁 for num in range(10): t1 = threading.thread(target=func,args=(num,)) t1.start()
因为lock锁如果有多层锁机制,会造成死锁
现象,所以有了rlock
(递归锁)
代码如下:
import time import threading lit = [] lock = threading.rlock() # 创建一个递归锁 def func(arg): # lock只能解开一个锁,会造成死锁线程 # rlock可以解开两个锁,解决了下面问题 lock.acquire() # 将该行代码以下代码锁起来,直到遇到release释放 lock.acquire() lit.append(arg) time.sleep(0.04) m = lit[-1] print(arg,m) lock.release() # 释放锁 lock.release() # 释放锁 for num in range(10): t1 = threading.thread(target=func,args=(num,)) t1.start()
import time import threading # 创建一个锁,这个可以支持你同时进行几次锁,默认为1次 lock = threading.boundedsemaphore(3) def func(arg): lock.acquire() print(arg) time.sleep(1) lock.release() for num in range(20): t = threading.thread(target=func,args=(num,)) t.start()
lock版本的生产者与消费者模式可以正常的运行,但是存在一个不足,在消费者中,总是通过while true死循环并且上锁的方式去判断钱够不够,上锁是一个很耗费cpu资源的行为,因此这种方式不是最好的,还有一种更好的方式便是使用threading.condition来实现,threading.condition可以在没有数据的时候处于堵塞等待状态,一旦有合适的数据了,还可以使用notify相关的函数来通知其他处于等待状态的线程。这样可以不用做一些无用的上锁和解锁的操作。可以提高程序的性能。首先对threading.condition相关的函数做个介绍,threading.condition类似threading.lock,可以在修改全局数据的时候进行上锁,也可以在修改完毕后进行解锁。以下将一些常用的函数做个简单的介绍:
# 方法一 import time import threading # 创建一个锁,这个可以支持你同时进行几次锁,默认为1次 lock = threading.condition() def func(arg): print('线程进来了') lock.acquire() lock.wait() print(arg) time.sleep(1) lock.release() for num in range(3): t = threading.thread(target=func,args=(num,)) t.start() while true: num = int(input('>>>:')) lock.acquire() lock.notify(num) lock.release() # 方法二 import time import threading # 创建一个锁,这个可以支持你同时进行几次锁,默认为1次 lock = threading.condition() def xxx(): print('来执行函数了') input('>>>:') return true def func(arg): print('线程进来了') lock.wait_for(xxx) print(arg) time.sleep(1) for num in range(3): t = threading.thread(target=func,args=(num,)) t.start()
import threading # 创建一个锁,这个可以支持你同时进行几次锁,默认为1次 lock = threading.event() def func(arg): print('线程进来了') lock.wait() # 变红灯 print(arg) for num in range(10): t = threading.thread(target=func,args=(num,)) t.start() input('>>>') lock.set() # 变绿灯 input('>>>') # 重新回归 红灯状态 lock.clear() for num in range(10): t = threading.thread(target=func,args=(num,)) t.start() input('>>>') lock.set() # 变绿灯 input('>>>')
线程安全:列表和字典就是线程安全;
为什么要加锁?
为每一个线程创建一个字典键值对,为数据进行隔离
示例代码:
import time import threading pond = threading.local() def func(arg): # 内部会为当前线程创建一个空间用于存储,phone = 自己的值 ,将数据隔离开 pond.phone = arg time.sleep(2) # 取当前线程自己空间取值 print(pond.phone,arg) for num in range(10): t = threading.thread(target=func,args=(num,)) t.start()
使用concurrent
来创建线程池,使用线程池可以有效的解决线程无止境的问题,用户每一次一个请求都会创建一个线程,这样线程一堆积也会造成程序缓慢,所以,线程池就可以帮我们解决这个问题,线程池可以设定,最多同时执行n个线程,由自己设定。
示例代码:
import time from concurrent.futures import threadpoolexecutor # 创建一个线程池,(最多同时执行5个线程) pool = threadpoolexecutor(5) def func(arg1,arg2): time.sleep(1) print(arg1,arg2) for num in range(5): # 去线程池申请一个线程,让线程执行func函数 pool.submit(func,num,8)
在线程中,访问一些全局变量,加锁是一个经常的过程。如果你是想把一些数据存储到某个队列中,那么python内置了一个线程安全的模块叫做queue模块。python中的queue模块中提供了同步的、线程安全的队列类,包括fifo(先进先出)队列queue,lifo(后入先出)队列lifoqueue。这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么都做完),能够在多线程中直接使用。可以使用队列来实现线程间的同步。相关的函数如下:
示例代码
#encoding:utf-8 from queue import queue import threading import time # q = queue(4) # 添加4个队列 # q.put(10) #第1个队列插入一条数据 # q.put(4) #第2个队列插入一条数据 # for x in range(4): # q.put(x) # # for x in range(4): # print(q.get()) # print(q.empty()) # print(q.full()) # print(q.qsize()) def set_value(q): index = 0 while true: q.put(index) index +=1 time.sleep(2) def get_value(q): while true: print(q.get()) def main(): q = queue(4) t1 = threading.thread(target=set_value,args=[q]) t2 = threading.thread(target=get_value,args=[q]) t1.start() t2.start() if __name__ == '__main__': main()
生产者和消费者模型解决了 不用一直等待的问题
使用queue创建队列
示例代码:
import time import threading from queue import queue q = queue() def producer(id): '''生产者''' while true: time.sleep(2) q.put('包子') print('厨师 %s 生产了一个包子'%id) def consumer(id): '''消费者''' while true: time.sleep(1) v1 = q.get() print('顾客 %s 吃了一个包子'%id) for produce in range(1,4): t1 = threading.thread(target=producer,args=(produce,)) t1.start() for consu in range(1,3): t2 = threading.thread(target=consumer,args=(consu,)) t2.start()
为什么创建线程?
为啥创建进程?
python
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
新手学习Python2和Python3中print不同的用法
Python基于os.environ从windows获取环境变量
网友评论